Công nghệUncategorized

Vấn đề cache trong ứng dụng Web

Trong một dự án gần đây, tôi đã thêm một tính năng mới thú vị cho phép người dùng nhập địa chỉ công ty của họ nếu nhà cung cấp bên thứ ba của chúng tôi không có nó trong hồ sơ. Tính năng này hoạt động hoàn hảo trên môi trường cục bộ của tôi, nhưng lại thất bại trong môi trường thử nghiệm. Nguyên nhân gốc rễ? Một vấn đề cache tinh vi khi gọi nhà cung cấp bên thứ ba. Trong bài viết này, tôi sẽ chia sẻ những gì tôi đã phát hiện và cách tôi giải quyết vấn đề.

Cache là gì?

 

Cache là một thành phần phần cứng hoặc phần mềm được sử dụng để lưu trữ tạm thời dữ liệu trong môi trường tính toán, được thiết kế để cải thiện hiệu suất bộ xử lý bằng cách cho phép truy cập dữ liệu nhanh hơn mà không cần lấy nó từ bộ nhớ trung tâm. Các bộ xử lý có các cấp độ cache khác nhau, cho phép CPU truy xuất dữ liệu từ các cache này thay vì bộ nhớ hệ thống.

Cache trình duyệt

Cache trình duyệt

Mỗi trình duyệt đều có một cache. Khi trình duyệt của bạn yêu cầu một tài nguyên từ máy chủ, nó sẽ kiểm tra cache của nó cho tài nguyên đó. Nếu tài nguyên được tìm thấy, trình duyệt sẽ sử dụng phiên bản được lưu trong cache thay vì thực hiện yêu cầu tới máy chủ. Nếu không, trình duyệt sẽ lấy tài nguyên từ máy chủ và sau đó lưu vào cache để sử dụng trong tương lai.

Ví dụ, khi Chrome cần tải xuống một hình ảnh, nó sẽ kiểm tra cache của nó trước. Nếu hình ảnh không có ở đó, Chrome sẽ thực hiện yêu cầu mạng để tải về.

Cache-control

 

Header “cache-control” trong các phản hồi HTTP hướng dẫn hành vi cache của trình duyệt. Một tài nguyên được coi là “fresh” (tươi mới) nếu nó vẫn còn hợp lệ và có thể tái sử dụng; nó trở nên “stale” (hết hạn) khi hết hạn và không thể tái sử dụng mà không qua kiểm tra.

Bạn có thể tìm thấy giá trị cache-control trong các yêu cầu HTTP sử dụng DevTools phần network, dưới “Response headers”.

Ví dụ về các chỉ thị cache-control:

  • No-cache: Tài nguyên có thể được lưu vào cache nhưng phải được kiểm tra lại với máy chủ gốc mỗi khi tái sử dụng.
  • Public/Private: Chỉ ra liệu phản hồi có thể được lưu vào cache chia sẻ hoặc cache cá nhân.
  • Must-revalidate: Tài nguyên có thể tái sử dụng khi còn tươi mới. Khi hết hạn, phải được kiểm tra lại trước khi tái sử dụng.
  • Max-age: Chỉ định thời gian tối đa (tính bằng giây) một tài nguyên vẫn còn tươi mới.

Tham khảo tài liệu MDN để biết danh sách đầy đủ các chỉ thị cache-control.

Kiểm tra tài nguyên hết hạn

Trình duyệt kiểm tra các tài nguyên được lưu trong cache bằng cách sử dụng các header như Last-Modified hoặc Etag. Header Last-Modified chứa ngày cuối cùng tài nguyên được sửa đổi. Nếu có sẵn, trình duyệt sẽ gửi một yêu cầu If-Modified-Since. Nếu tài nguyên không thay đổi, máy chủ sẽ phản hồi với mã trạng thái 304 Not Modified, cho biết phiên bản lưu trong cache có thể được sử dụng.

Tương tự, header Etag là một định danh duy nhất cho một tài nguyên. Trình duyệt gửi một yêu cầu If-None-Match với Etag. Máy chủ sẽ phản hồi với mã trạng thái 304 hoặc tài nguyên đã cập nhật, làm cho việc kiểm tra hiệu quả hơn vì phản hồi 304 nhỏ hơn.

Bạn có thể kiểm tra các tài nguyên được lưu trong cache trong DevTools bằng cách tìm mã trạng thái 304 hoặc xem dữ liệu lưu trong cache trong tab Application.

Caching trong ứng dụng Java

Caching trong các ứng dụng web có thể bao gồm:

  • Caching các yêu cầu HTTP đến các ứng dụng bên thứ ba.
  • Caching các truy vấn cơ sở dữ liệu.
  • Caching kết quả của các phương thức trên máy chủ.

Để giải quyết vấn đề của tôi, tôi tập trung vào tùy chọn thứ ba. Đối với các ứng dụng web Java, các giải pháp caching phổ biến bao gồm Memcached, Varnish, Ignite và Redis. Tôi đã sử dụng Redis, vốn đã là một phần của dự án của tôi.

Redis

Redis (Remote Dictionary Server) là một cơ sở dữ liệu key/value mã nguồn mở nổi tiếng về tốc độ, sự đa dạng của các loại dữ liệu và khả năng lưu trữ dữ liệu. Trong Java, hai thư viện chính của Redis là Lettuce và Jedis. Lettuce thiết lập kết nối với máy chủ, trong khi Jedis xử lý caching trong mã.

Để kích hoạt caching, hãy tạo một tệp cấu hình với các chú thích @EnableCaching@Configuration. @EnableCaching quét các phương thức có chú thích @Cache, tạo ra các proxy để quản lý hành vi caching. RedisCacheManager xử lý các cuộc gọi phương thức được lưu trong cache, sử dụng RedisTemplate để thực hiện các thao tác cache như đọc, ghi hoặc kiểm tra sự tồn tại của giá trị.

Trước khi sử dụng RedisTemplate, các đối tượng phải được tuần tự hóa với RedisSerializer, cho phép Redis hiểu các đối tượng Java. Điều này hoàn thành quy trình làm việc caching của Redis.

Các thực hành tốt nhất khi caching với Redis

Quản lý khóa

Redis là một cơ sở dữ liệu key-value, sử dụng các tham số của phương thức để tạo khóa. Với các tham số đơn giản, các khóa rất dễ hiểu. Tuy nhiên, các tham số phức tạp yêu cầu các khóa tùy chỉnh bằng cách sử dụng tham số KeyGenerator trong các chú thích @Cacheable.

Đảm bảo dữ liệu tươi mới

Đảm bảo dữ liệu trong cache luôn tươi mới. Ví dụ, nếu một người dùng thay đổi địa chỉ của họ, hãy cập nhật hoặc xóa dữ liệu trong cache. Sử dụng RedisTemplate cho các thao tác CRUD hoặc các chú thích như @CachePut@CacheEvict.

public class UserService {

    ...

  @CachePut(value = "user", key = "#userId")

  public User patchUserInfo(User user, Long userId) {

    return to(userRepository.save(to(user, userId)));

  }

}




public interface UserRepository extends JpaRepository<UserEntity, UUID> {

  @CacheEvict(cacheNames = "user", key = "#userEntity.userId")

  @Override

  UserEntity save(UserEntity userEntity);

}

 

Kết luận

Sử dụng các header cache-control, trình duyệt có thể lưu trữ các tài nguyên như hình ảnh, script và các phản hồi HTTP. Trong Java, Redis có thể dễ dàng caching dữ liệu trong toàn bộ ứng dụng của bạn. Mặc dù caching có thể cải thiện hiệu suất bằng cách giảm các cuộc gọi lặp lại, việc triển khai cẩn thận là rất quan trọng để tránh các lỗi. Caching không phải là một giải pháp toàn diện, nhưng là một công cụ mạnh mẽ khi được sử dụng đúng cách.

Shares:

Related Posts

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *