Đổi bộ nhớ lấy hiệu năng!

Có thể mọi người đã nghe đến ThreadPool, HikariCP connection pool, hay c3p0 connection pool của hibernate rồi đúng không? Chúng đều là những ứng rất cụ thể cho ObjectPool pattern. Nhưng chúng ta có thể thấy không có nhiều thứ ứng dụng connection pool pattern cho lắm ngoài những thứ kể trên Cũng giống như Flightweight Pattern, nhưng ở một phạm vị hẹp hơn, Object Pool sinh ra cho mục tiêu: hy sinh bộ nhớ để đổi lấy hiệu năng thông qua việc khởi tạo rất nhiều các đối tượng ban đầu và cố gắng tái sử dụng các đối tượng này ở runtime.

Các thành phần bên trong

Nhưng khác với Flightweight ObjectPool thực tế là wrap lại của 1 cái queue (và đã là queue thì khó có thể nào dùng để quản lý đối tượng, chúng ta không thể có những hàm kiểu getById, getByType được), lúc đầu nó được khởi tạo trước số đối tượng, mỗi lần lấy ra đối tượng là sẽ lấy ra khỏi queue cho đến khi nào cái queue đó hết thì lại tạo ra đối tượng mới, khi nào sử dụng xong lại nhét vào queue. Có 2 tham số quan trọng cho 1 ObjectPool.

  1. minObjects: là số lượng tối thiểu object sẽ được tạo lúc đầu khi pool được khởi tạo
  2. maxObjects: là số lượng tối đa object sẽ được tạo trong quá trình chạy, và khi số lượng object được tạo vượt quá maxObjects thì những đối tượng này sẽ bị huỷ mà không được cho vào queue.

Cách thức sử dụng

Trước đây mình đã cố gằng dùng ObjectPool để tái sử dụng Session cho ezyfoxserver, nhưng đã thất bại, vì sao vậy? Đây là bài học rất đáng nhớ, vì mình có 2 chỗ liên quan đến đối tượng session, đó là SessionPool và đối tượng User, khi mình trả lại Session cho pool thì user vẫn còn xử lý nốt Session và rất còn nhiều Thread và các đối tượng quản lý khác (SessionManager, GameSessionManager, …) khác vẫn đang xử lý Session điều này làm cho mọi thứ loạn hết cả lên. Nên theo kinh nghiệm của mình, ObjectPool chỉ nên được sử dụng kiểu này:

Connection connection = connectionPool.getConnection();
try {
 connection.createStatement().executeQuery("SELECT * FROM table");
}
finally {
 connectionPool.returnConnection(connection);
}

Nghĩa là chúng ta sẽ lấy Object ra khỏi Pool, sử dụng và trả lại Pool ngay sau đó, không nên và cũng không bao giờ nên chia sẻ đối tượng của Pool giữa các Thread hay các đối tượng quản khác nhé, sẽ rất dễ để chúng ta gặp lỗi.

Không còn quá quan trọng

Chính vì ObjectPool tương đối khó dùng nên với tốc độ của máy tính ngày nay, ObjectPool không còn được ưa chuộng nữa, nó chỉ được sử dụng trong những trường hợp khởi tạo đối tượng quá chậm chạp, các đối tượng trong Pool phải là ThreadSafe (nên là Immutable - không hoặc ít thay đổi trạng thái) và cần tái sử dụng nhiều lần, có lẽ Connection là đối tượng phù hợp nhất rồi. Về phía client (game, app), hãy cân nhắc thật cẩn thận việc sử dụng ObjectPool, theo mình Flightweight là một sự lựa chọn phù hợp hơn

Tham khảo:

  1. Lý thuyết
  2. ConnectionPool example