Sử dụng Keyset Pagination – Seek bằng  Blaze Persistence

Nếu bạn chưa nghe về khái niệm này, có thể đọc ở đây http://quochung.cyou/toi-uu-truy-van-pagination-phan-trang-su-dung-spring-boot-java/

 Blaze Persistence

Tuy nhiên bạn có thể sử dụng  Blaze Persistence để làm điều đó và có thể hoạt động plug-n-play với JPA luôn.

Tạo một CriteriaBuilderFactory

image 15 - quochung.cyou PTIT

Lấy tập dữ liệu Top-N trên cùng

image 16 - quochung.cyou PTIT

Để lấy trang đầu tiên, ta sử dụng một query Top-N như sau.

  • Bạn có thể tùy chỉnh orderByAsc để căn theo sort by tập dữ liệu cần có
  • pageSize quy định số lượng số phần tử cần lấy
  • withKeysetExtraction sẽ yêu cầu Blaze Persistence để lưu các thông số để có thể lấy dữ liệu cho query N phần tử tiếp theo
image 17 - quochung.cyou PTIT
  • PagedList implement từ List có sẵn của Java, với một số hàm để bạn có thể extract dữ liệu
image 18 - quochung.cyou PTIT

Output:

image 19 - quochung.cyou PTIT

Lấy trang tiếp theo

image 20 - quochung.cyou PTIT

  • Page tiếp theo sẽ sử dụng keysetPage ta đã có trước đó, để có thể thực hiện skip lượng row cần thiết để lấy tập dữ liệu tiếp theo

Thử nghiệm kết quả

image 22 - quochung.cyou PTIT
  • Sử dụng cách tương tự để nhảy sang các trang kế tiếp

Tham khảo:

Dependency Inversion là gì, biến N-Layered thành DDD

N-Layered

Bất kì mô hình hệ thống nào đều nhắm đến giúp code của bạn được tổ chức tốt hơn, đẹp hơn. Khi mà nhu cầu của người dùng ngày càng cao, độ phức tạp của hệ thống càng gia tăng, mô hình hệ thống của bạn cũng cần phải bắt kịp chúng. Với một số dự án nhỏ, có thể bạn chỉ cần thêm thẳng code vào controller, tuy nhiên, ở các hệ thống lớn hơn bạn sẽ cần một số cách tiếp cận hợp lí hơn.

Đa số hệ thống chỉ cần cấu trúc N-Layered (N tầng) với 3 tầng cơ bản:

  • User Interface (UI) — chịu trách nhiệm tương tác với người dùng (giao diện)
  • Business logic layer (BLL)— Xử lí các logic chính của hệ thống
  • Data Access layer (DAL) — Là tầng giao tiếp với dữ liệu như database, …
1 pd5bfxtxV9AK0 QlIOK6Vw - quochung.cyou PTIT

VỚi một hệ thống như vậy, đa số vấn đề của bạn sẽ giải quyết theo 1 chiều thông thường. Các tầng sẽ phụ thuộc trực tiếp vào nhau, sau đó gọi dần xuống tầng bên dưới.

image 9 - quochung.cyou PTIT

Ví dụ một luồng:

  • User click vào 1 nút trên tầng UI của website. Tầng UI sau đó gọi 1 API vào Bussiness Logic, bussiness logic gọi xuống Data Access để lấy dữ liệu bài viết ở database, sau đó ở bussiness logic sẽ xử lí dữ liệu đó (format, thêm thắt ngày tháng, ….), rồi cuối cùng trả về tầng trên cùng UI.

Tuy rằng kiến trúc N-Layered giúp ta xử lí đa số vấn đề, giúp code gọn hơn, clean hơn, tổ chức các chức năng liên quan thành nhiều layer khác nhau, dễ cài đặt, nhưng chúng cũng có một số vấn đề:

  • Khi hệ thống lớn dần, thường các logic sẽ được xử lí ở tầng Data Access (database), chứ không phải ở Business Logic. Điều này khiến lập trình viên thường tập trung vào tầng db hơn là tầng logic chính.
  • Tầng Data Access dần làm mọi việc, khó để thay đổi, vì các tầng trên đều nhảy vào nó.
  • Các tầng liên kết với nhau chặt chẽ, và vài thay đổi ở tầng Data Access sẽ làm hỏng các phần khác.

Ví dụ: Sau khi code nhiều ngày đêm, bạn gửi sản phẩm là một trang đọc báo cho khách hàng. Trang đọc báo này có cơ chế như sau:

  • Tầng UI: Hiển thị một bài viết
  • Business Logic: Gọi xuống database lấy dữ liệu các bài viết
  • Data Access: Làm việc với database, lấy các dữ liệu bài viết ra từ một database MySQL chẳng hạn.

Tầng Business Logic chứa các hàm:

  • layBaiViet() -> dataAccess.layBaiVietDb()

Tầng Data Access sẽ chứa hàm bên trên, nơi Business gọi xuống, và thực hiện các tác vụ, vì lúc này các model, object đang nằm ở tầng Data, chứ không ở tầng Business:

  • layBaiVietDb() -> locBaiViet() -> suaNgayThang() -> ….

Do các model, entity ta làm việc với db đều chứa hết ở tầng này, nên ta cần xử lí các hàm ở tầng Data Access. Và kể cả có chuyển các hàm này lên tầng bên trên, thì nhìn chung các Model đều vẫn chứa ở Data Access. Chỉ cần mình thay đổi ví dụ Model ở Data Access một hàm mới, bên trên đều phải thay đổi.

Tưởng tượng lúc này, một ngày đẹp trời, khách hàng của bạn muốn chuyển DB sang MongoDB chẳng hạn, hoặc chuyển sang abcxyz gì đó. Lúc này bạn lại phải sửa hết code ở tầng Data Access cho hợp DB mới, và việc sửa ở đây cũng dẫn đến sửa các tầng bên trên

DDD – Domain Driven Design

Mô hình trên cho rằng, ta nên tập trung vào phần Business, hay còn gọi là Domain là phần core, cốt lõi của hệ thống hơn, thay vì Data Access. Data Access chỉ nên tập trung vào lấy dữ liệu, cập nhật dữ liệu, …. , còn xử lí logic nên nằm ở Domain. Tuy nhiên, với cấu trúc như thông thường khi mà chiều phụ thuộc, gọi xuống đang đi từ trên xuống dưới, ta buộc phải dồn logic xuống dưới cùng. Lúc này, ta có một kĩ thuật mới (Dependency Inversion – Đảo chiều sự phụ thuộc) , đây cũng là chữ cái “D” trong S.O.L.I.D

image 10 - quochung.cyou PTIT

Điều gì đã thay đổi ? Ta vẫn giữ những cấu trúc tầng, cho phép code được tổ chức, chia thành các khối, giữ nguyên các điểm tốt của N-Layered, tuy nhiên với một vài cải tiến:

  • Business Logic/Domain sẽ là phần lõi, ở giữa của hệ thống, được dùng ở các phần khác
  • Business Logic giờ mới là phần khó thay đổi. Điều này có thể chấp nhận, bạn hãy tưởng tượng, có thể hệ thống bạn sẽ đổi database khác, đổi cách lấy dữ liệu, hay giao diện khác, còn logic trung tâm của hệ thống (ví dụ hệ thống ngân hàng thì logic trung tâm là xử lí tiền, …) thì không thể đổi được (trừ khi công ty sập)
  • Mỗi thay đổi của tầng Business/Domain sẽ cần đổi ở tầng khác, điều này cũng đúng hơn với tự nhiên, khi mà concept cốt lõi là logic của ứng dụng, mục tiêu của ứng dụng thay đổi thì mới thay đổi các tầng ocn kia.

Nhưng làm sao để có thể đảo chiều sự phụ thuộc?

Trước tiên, hãy xem cách triển khai thông thường của N-Layered

image 11 - quochung.cyou PTIT
  • OrderService là một module xử lí các order (đơn hàng) cho hệ thống shop của bạn. Nhưng chúng chưa có dữ liệu, vì vậy chúng cần gọi xuống SqlOrderRepository là một module lấy dữ liệu từ database. Như vậy, OrderService phụ thuộc vào SqlOrderRepository
image 12 - quochung.cyou PTIT
  • Thông thường, người ta sẽ viết OrderService phụ thuộc vào một IOrderRepository (interface), để giúp việc thay thế dễ dàng hơn (phụ thuộc 1 interface ảo, chứ không phải 1 class)
  • Lúc này ta chỉ cần di chuyển interface trên về business
image 13 - quochung.cyou PTIT
  • Lúc này, ta sẽ quy định các method của repository, các hàm như kiểu getOrderByName (lấy dữ liệu từ tên), update, create, …. OrderService phụ thuộc trực tiếp vào repository này (Vẫn ở tầng Business)
  • Lúc này tầng Data Access, các class xử lí dữ liệu thực sự sẽ implement lại repository từ tầng Business, implement lại các hàm trên, nơi viết logic thực sự.
  • Lúc này, tầng Data Access đang phụ thuộc ngược vào tầng Bussiness
  • Rõ ràng, OrderService không cần quan tâm Data Access có thể là database nào nữa, nó có thể là class MySQL, Môngo,…. , chỉ cần biết chúng phải implement interface Repository có ở Business, và việc xử lí logic thì data access xử lí sao thì business không cần quan tâm.
  • Việc gắn các class ở dataaccess vào business có thể thông qua các IoC Container (Ví dụ như Spring IoC – Inversion of Control)
image 14 - quochung.cyou PTIT

  • Cách làm trên được gọi là Dependency Inversion (đảo ngược sự phụ thuộc) khi mà sự phụ thuộc, nơi xử lí được đảo ngược lại.

Tham khảo:

Tối ưu truy vấn Pagination (phân trang) sử dụng Spring Boot (Java)

image 4 - quochung.cyou PTIT

Phân trang là gì, tại sao cần phân trang

Phân trang đơn giản là ta sẽ chia một tập dữ liệu lớn và truyền cho user thành từng phần nhỏ hơn. Hãy tưởng tượng, bạn đang thiết kế một hệ thống backend cho phía frontend của một trang tin tức, phía FE muốn một lệnh API để có lấy dữ liệu các bài viết từ database của bạn.

image 5 - quochung.cyou PTIT

Bài toán sẽ khá đơn giản nếu bạn chỉ có 10-20 bài viết, bạn chỉ cần làm theo cách truyền thống, lấy dữ liệu từ database từ backend, gửi một file json chứa dữ liệu cho FE, mọi thứ nghe có vẻ đơn giản.

Tuy nhiên khi tập dữ liệu của ta ngày càng lớn, ví dụ, lúc này 1 Model bài viết có rất nhiều trường (ảnh đại diện, chuyên mục, thời gian đăng, ….), và ta có hàng 1000-10000 bài viết, việc trả 1 lần tập dữ liệu lớn như thế sẽ làm chậm ở 2 chỗ:

  • Lấy toàn bộ dữ liệu từ database tốn thời gian
  • Truyền 1 lần dữ liệu lớn như vậy sẽ rất nặng, tốn tiền mạng thật sự của user (tải 1 file lớn mỗi lần), và lúc tải 1 file lớn như thế cũng rất chậm, tốn thời gian, gây khó chịu người dùng

Tổng kết 1

Khi đó, ta có thể sử dụng đến phân trang, và mỗi lần người dùng chỉ có thể xem 1 trang, ta chỉ gửi khoảng 10-15 bài viết 1 lần thôi, khi người dùng sang trang khác ta sẽ gửi tập dữ liệu khác, khá đơn giản.

Triển khai phân trang bằng Spring Boot

Triển khai Model

image 6 - quochung.cyou PTIT

Triển khai Repository

Để có thể truy cập các JPA Entity trên từ database, ta sẽ tạo 1 PostRepository

image 7 - quochung.cyou PTIT

JPARepository đã kế thừa sẵn một interface là PagingAndSortingRepository của Spring hỗ trợ việc phân trang và sắp xếp. Dù là một interface, không có implemention, nhưng khi chương trình khởi chạy, Spring sẽ tự động generate các code implemention thật cho chúng ta, thực thi các thao tác với database, và chúng ta chỉ cần define các method có sẵn thôi.

Phân trang

Do Spring đã làm đa số các phần code, giờ ta chỉ cần triển khai thêm 2 việc

  • Tạo một PostPageRequest class, implement từ Pageable interface của Spring
  • Truyền tham số cần tìm cho PostPageRequest
image 9 - quochung.cyou PTIT

Khi implement interface Pageable, ta sẽ phải triển khai một số hàm. Lúc này class PostPageRequest sẽ như một object “trang” , kiểu trang 5 thì là một object Trang, trang 6 là một object khác. Từ object trang đó ta có thể lấy trang tiếp theo, trang phía trước, các bài viết trong trang,….

offset và limit

Bạn có thể thấy trong code trên có các tham số offset và limit. Hai tham số trong sql có ý nghĩa như sau

  • offset x : Lùi x kết quả từ dãy kết quả trả ra
  • limit y: Từ danh sách kết quả trả ra, lấy y kết quả đầu tiên.
  • Ví dụ: ta có các bài viết đánh số từ 1-100. offset 5 thì ta sẽ có danh sách là 6-100. limit tiếp 10 thì ta có danh sách 6-16…

Nếu bạn đã hiểu định nghĩa offset và limit, hãy thử đọc lại code bên trên, bạn sẽ dễ dàng hiểu được các method lấy trang tiếp theo, lấy trang trước, …. đang làm gì.

image 10 - quochung.cyou PTIT

Tổng kết 2

Như vậy, ta đã triển khai được phân trang bằng Spring Boot, tuy nhiên liệu như vậy đã tối ưu cho trang web của bạn chưa?

Tối ưu limit và offset trong MySQL

image 11 - quochung.cyou PTIT
image 12 - quochung.cyou PTIT
OffsetQuery Duration (ms)
01
501
100013
10000150
25000500
50000930
1000001750

Những biểu đồ và bảng

Ta có thể thấy, bằng việc dùng offset để lùi kết quả đi một đoạn, rồi lấy limit để lấy lượng bài ở trang đó nghe có vẻ rất đơn giản, dễ hiểu. Nhưng thực tế trong MySQL chúng được thực hiện như sau:

…the rows are first sorted according to the <order by clause> and then limited by dropping the number of rows specified in the <result offset clause> from the beginning…

…các dòng đầu tiên được sắp xếp (ví dụ theo id, thời gian đăng bài…) sau đó xóa x hàng đầu tiên được yêu cầu khi sử dụng offset
image 13 - quochung.cyou PTIT

Nếu bạn nghĩ kĩ, câu lệnh offset chỉ nhận đúng 1 tham số: lượng dòng bị bỏ qua cho đến tập kết quả muốn nhận.

Cách duy nhất hệ thống database có thể làm điều này là lấy toàn bộ dữ liệu cần tìm, sau đó ném đi x hàng đầu bạn đã đặt ra yêu cầu. Khi offset đủ lớn, lượng công việc cho database sẽ rất nhiều và thời gian để truy vấn sẽ tăng khó kiểm soát.

Khi sử dụng offset, ta mở trang đầu tiên, trang thứ 2, thời gian mất chỉ 1ms (1 phần nghìn giây), gần như không có vấn đề gì.

Trang thứ 10000, 150ms, vẫn chưa nhận ra điều gì quá lo ngại

100000 1750ms, 1,75 giây.

Ta có thể thấy, nếu người dùng mở 1 trang càng xa, hiệu suất của trang sẽ càng giảm, việc một user mở trang thứ 10000 có thể gây vấn đề hiệu năng hơn nhiều cho database hơn 100 người dùng khác mở trang 1

Hướng khắc phục

Trước hết, ta cần tìm hiểu xem những hệ quản trị cơ sở dữ liệu đang làm gì để sắp xếp dữ liệu của chúng ta. Ta sẽ assume hệ cơ sở dữ liệu đang sử dụng một B-Tree để index database (một bản nâng cấp của cây nhị phân cân bằng).

Nếu bạn chưa từng nghe đến cụm từ trên, bạn có thể tìm hiểu ở link sau:

Lúc này database của chúng ta lưu dữ liệu theo dạng như sau:

image 14 - quochung.cyou PTIT

Khi ta sử dụng offset 0, limit 5 để lấy 5 bài viết đầu tiên chẳng hạn, database sẽ chạy các kết quả sau

SELECT * FROM my_table ORDER BY id LIMIT 5
image 15 - quochung.cyou PTIT

Tuy nhiên, với offset, sau khi offset 5 và limit 5, ta có

image 16 - quochung.cyou PTIT

Như vậy, ta phải đi qua 5 cái đầu trước, bỏ dần nó, rồi sau đó mới limit 5 cái sau. Khá là tốn thời gian

Vấn đề: Database không biết điểm khởi đầu của trang tiếp theo ở đâu, vì vậy nó cứ phải bỏ dần các hàng phía trước cho đến khi đến được vị trí chỉ định

=> Khắc phục: Ta nhớ xem vị trí lần cuối là ở đâu ?

Kĩ thuật: keyset pagination and seek method

Ở một số trang web, bạn sẽ thấy, bạn không thể đi đến thẳng trang cuối, hoặc nhảy đến 1 trang bất kì, mà thông thường sẽ có nút để sang trang kế và trang phía trước. Như vậy ta có thể assume rằng:

Người dùng sẽ chỉ mở trang 10 sau khi mở trang 9.

Vậy, ta chỉ cần nhớ vị trí cuối cùng của bài viết ở trang 9 là ở id bao nhiêu, rồi dùng WHERE để truy vấn từ điểm đó, chứ không cần bỏ dần để đi đến điểm đó nữa
SELECT * FROM my_table WHERE id > 21 ORDER BY id LIMIT 5
image 17 - quochung.cyou PTIT

Ví dụ: Sort theo thời gian

SELECT *
FROM my_table
WHERE (update_date = '2017-12-21' AND id > 21) 
    OR update_date > '2017-12-21'
ORDER BY update_date,id LIMIT 5

Cơ bản là ta sẽ chỉ lấy các bài viết có cùng thời gian đăng như bài viết cuối và id > , hoặc thời gian lớn hơn bài viết cuối

image 18 - quochung.cyou PTIT

Tổng kết

  • Phương pháp keyset pagination and seek giúp tối ưu việc phân trang cho các bài toán lớn hơn rất nhiều (Tham khảo biểu đồ trên)
  • Triển khai trong Java (JPA/Hibernate)

Tuy nhiên, phương pháp này sẽ có thêm các vấn đề sau

  • Ta cần thay đổi lại code của hệ thống để triển khai phương pháp này, cần phải nhớ hàng cuối cùng hiện tại, làm code phức tạp hơn, khó quản lí hơn
  • Cần phải index database theo id, pubdate, ….
  • Các hàng tìm kiếm cần được sắp xếp, không được null
  • Không thể đi trực tiếp đến trang 500,1000,… được, mà chỉ có thể sang trang kế tiếp do ta cần nhớ điểm cuối từ trang phía trước

Có thể tham khảo thêm Spring HateOAS hỗ trợ thêm vấn đề này

Các nguồn tham khảo:

Tại sao cần viết getter, setter cho các Class?

Getter, Setter là gì

Getter, Setter là các method để lấy dữ liệu và cập nhật dữ liệu cho các trường của 1 class

image - quochung.cyou PTIT

Như ví dụ trên, thay vì viết

Account taiKhoan;
taiKhoan.ID = 10;
//Thay vì viết như trên để cập nhật dữ liệu cho một object Account có ID thành 10, ta sẽ viết rằng

taiKhoan.setID(10);

Tại sao cần phải tạo ra getter, setter như vậy?

Để ngắn hơn chăng? Để đơn giản hơn? Cũng không phải, cách viết hàm trong một số trường hợp còn làm code dài hơn và khó đọc hơn là set thẳng trực tiếp, lí do thực sự là, việc này áp dụng tính chất đóng gói (encapsulation) của OOP.

image 1 - quochung.cyou PTIT

Tính đóng gói (Encapsulation) từ Getter, Setter

Khi sử dụng getter, setter, ta sẽ để access cho các biến trong class thành private, và để cho các phương thức get, set là public. Lúc này, để có thể truy cập hay cập nhật dữ liệu của các biến của 1 object, ta chỉ còn 1 con đường duy nhất thông qua getter setter

Điều này giúp cho ta có thể thêm một lớp validation (xác thực) dữ liệu khi tác động đến dữ liệu của 1 object. Ví dụ, một con mèo không thể có chiều cao là 0 được, một đồ vật phải luôn có trọng lượng,….

image 2 - quochung.cyou PTIT
image 3 - quochung.cyou PTIT

Do lúc này, cách duy nhất để thay đổi chiều cao của con mèo chỉ có thể qua setHeight, ta luôn đảm bảo dữ liệu của chúng ta được xác minh.

Tổng kết

  • Việc sử dụng getter, setter và sử dụng access modifier (private) cho các biến và public cho getter/setter giúp ta đóng gói class, chỉ để lại duy nhất 1 con đường để truy cập/cập nhật
  • Khi chỉ còn 1 con đường, lập trình viên có thể thêm 1 lớp xác thực để tránh dữ liệu bị cập nhật xấu, sai.

Tham khảo: Head First Java 3rd Edition

Tìm hiểu SOLID – 5 nguyên tắc thiết kế hướng đối tượng bằng Java

Ngôn ngữ lập trình Java

Java được biết đến là ngôn ngữ lập trình bậc cao, hướng đối tượng và giúp bảo mật mạnh mẽ, và còn được định nghĩa là một Platform. Java được phát triển bởi Sun Microsystems, do James Gosling khởi xướng và ra mắt năm 1995. Java vẫn được sử dụng rất nhiều trong các dự án công nghệ mới. Tên ban đầu của Java là OAK, sau đó được Sun Microsystem đổi tên vào năm 1995 và tập trung phát triển các sản phẩm theo trend www (world wide web). Năm 2009, Java được mua lại bởi Oracle.

Các nguyên tắc thiết kế hướng đối tượng – SOLID

image 18 - quochung.cyou PTIT

SOLID nghĩa là gì

Nguyên tắc SOLID là một phương pháp tiếp cận hướng đối tượng trong thiết kế cấu trúc phần mềm được sử dụng trong Java. Robert C. Martin là người đã đưa ra ý tưởng này (còn được biết đến với tên Uncle Bob). Năm nguyên tắc này đã làm thay đổi toàn bộ lĩnh vực lập trình hướng đối tượng, cũng như cách các phần mềm được viết ra. Nguyên tắc SOLID cũng đảm bảo rằng phần mềm có tính chất mô-đun (dễ tái sử dụng và dùng trong nhiều vị trí), dễ hiểu, dễ gỡ lỗi và dễ refactor (tái cấu trúc, cập nhật).

  • S: Single responsibility principle – Nguyên tắc một chức năng
  • O: Open-closed principle – Nguyên tắc đóng và mở
  • L: Liskov substitution principle – Nguyên tắc thay thế
  • I: Interface segregation principle – Nguyên tắc chia nhỏ
  • D: Dependency inversion principle – Nguyên tắc đảo ngược phụ thuộc

Lí do nên áp dụng các nguyên tắc SOLID

  • Clean: Nguyên tắc SOLID giúp code clean, dễ nhìn hơn và chuẩn hoá format của code để nhiều người hiểu hơn.
  • Dễ bảo trì: Code dễ bảo trì, sửa lỗi hơn.
  • Tính co giãn: Dễ dàng refactor, tái cấu trúc lại code. Dễ dàng phát triển các tính năng mới sau này
  • Tối ưu: Giảm bớt các code dư thừa.
  • Kiểm nghiệm: Viết unit test dễ hơn
  • Dễ đọc: Giúp code đọc dễ hiểu hơn
  • Độc lập: Code hoạt động độc lập, ít phụ thuộc các phần khác, giảm thiểu lỗi
  • Tái sử dụng: Code được chia nhỏ và độc lập như các module, dễ dàng sử dụng lại.

SOLID trong Java

image 12 - quochung.cyou PTIT

Nguyên tắc 1: Single responsibility principle – Nguyên tắc một chức năng

image 13 - quochung.cyou PTIT

Nguyên tắc này được phát biểu như sau:

Một class chỉ nên giữ 1 trách nhiệm duy nhất, chỉ có thể sửa đổi class với 1 lý do duy nhất.

A class should have one and only one reason to change, meaning that a class should have only one job.

Theo nguyên lí này, mỗi class chỉ nên có một vai trò duy nhất. Tức là bạn có thể để 1 class có rất nhiều chức năng, làm đủ thứ, với nhiều method khác nhau. Tuy nhiên việc nhét toàn bộ chức năng vào 1 class khiến code khó bảo trì, khó hiểu hơn, và xử lí một phần nhỏ của cả một class chứa rất nhiều chức năng này có thể làm lỗi toàn bộ các chức năng khác.

Hãy thử xem một class sau:

image 14 - quochung.cyou PTIT

Việc cho toàn bộ các phương thức gộp vào 1 class NguoiChoi như này đã vi phạm quy tắc, thực hiện rất nhiều thay đổi chỉ trong 1 class như lấy dữ liệu từ database, chuyển sang json để trả về, di chuyển nhân vật, …. Sau này khi nâng cấp thêm chức năng, class này ngày càng phình to ra. Khiến cho việc bảo trì, nâng cấp, test, …. trở lên khó khăn hơn sau này.

Thay vì vậy, ta có thể chuyển thành như sau

image 15 - quochung.cyou PTIT
image 16 - quochung.cyou PTIT
image 17 - quochung.cyou PTIT
luong srp - quochung.cyou PTIT
  • Lúc này mỗi class sẽ độc lập hơn và các luồng hoạt động cũng sẽ rõ ràng hơn, khi có lỗi xảy ra hay cần nâng cấp chức năng, bạn có thể dễ dàng sửa đổi vào các class trong 1 luồng chứ không phải thay đổi hay thêm mọi thứ vào 1 class và khiến chúng phình to hơn nữa.
  • Tuy số lượng class nhiều hơn những việc sửa chữa sẽ đơn giản hơn, dễ dàng tái sử dụng hơn, class ngắn hơn nên cũng ít bug hơn.
  • Một số ví dụ về nguyên tắc SRP cần xem xét có thể cần được tách riêng bao gồm: Persistence, Validation, Notification, Error Handling, Logging, Class Instantiation, Formatting, Parsing, Mapping, …

Nguyên tắc 2: Open-closed principle – Nguyên tắc đóng và mở

Nguyên tắc này được phát biểu như sau:

Có thể thoải mái mở rộng 1 class, nhưng không được sửa đổi bên trong class đó.

Objects or entities should be open for extension, but closed for modification.

image 19 - quochung.cyou PTIT

Nghe qua thấy nguyên lý có sự mâu thuẫn do thường chúng ta thấy rằng dễ mở rộng là phải dễ thay đổi, đằng nay dễ mở rộng nhưng không cho thay đổi. Thực sự theo nguyên lý này, chúng ta không được thay đổi hiện trạng của các lớp có sẵn, nếu muốn thêm tính năng mới, thì hãy mở rộng class cũ bằng cách kế thừa để xây dựng class mới. Làm như vậy sẽ tránh được các tình huống làm hỏng tính ổn định của chương trình đang có.

Theo nguyên tắc này, sau khi thiết kế một class với một số chức năng nhất định, cần đảm bảo các chức năng này hoạt động trơn tru trong tương lai, tránh sửa đổi thêm sau này. Như vậy, class luôn “đóng – closed” cho các sửa đổi vào các chức năng đã được thiết kế trước, nhưng lại phải “mở – open” để mở rộng tính năng hơn, để mở thì có 1 số cách phổ biến như:

  • Tạo ra một class kế thừa
  • Viết lại chức năng hàm đó từ class cha
  • Nâng cấp chức năng/hàm của class cha ở class con

Lấy ví dụ như sau:

image 21 - quochung.cyou PTIT

Với cách thiết kế như trên, khi ta có các class con kế thừa từ class cha NguoiChoi, và cần kiểm tra xem class con có hệ là gì, hay ví dụ ta cần tạo thêm nhiều class con khác tương tự, ta lại phải thêm nhiều if else vào class gốc. Thay vào đó, ta nên thiết kế như sau:

image 22 - quochung.cyou PTIT

Lúc này, khi cần nâng cấp thêm nhiều hệ mới cho hệ thống, ta chỉ cần tạo các class con và sử dụng chức năng của class chính, không cần thực hiện trực tiếp vào class chính nữa.

  • Lợi ích của nguyên lý này là đôi khi chúng ta cần sử dụng các class từ các nguồn thư viện thứ 3, hoặc từ chính các thư viện có sẵn trong Java. Chúng ta có thể dễ dàng extend và tạo các class con mới kế thừa từ class cha để phục vụ cho một mục đích, chức năng mới của dự án, mà không cần quá lo lắng về class cha sẽ bị lỗi do ta đã không sửa đổi chúng.
  • Tuy nhiên, việc kế thừa class cha có thể dẫn tới việc chức năng các class con lại quá khác nhau và không có chung ý nghĩa, nên chú ý vào ý nghĩa của các chức năng, tránh tạo ra quá nhiều class dẫn xuất. Mặc dù những sửa đổi nhỏ trong class thường không ảnh hưởng, chúng ta cũng cần phải test cẩn thận. Và đó là lý do chính tại sao chúng ta cần phải viết test case cho các chức năng, để có thể nhận thấy hành vi không mong muốn xảy ra trong code.
  • Lúc này, ta có thể sử dụng interface như các bản thiết kế cha để có thể làm các chức năng mở rộng sau này, việc sử dụng interface cũng giúp code đạt thêm tính “Loose coupling” – “liên kết lỏng” hơn và tránh sự phụ thuộc quá chặt chẽ vào các class.

Nguyên tắc 3: Liskov substitution principle – Nguyên tắc thay thế

Barbara Liskov đã đưa ra nguyên tắc Liskov Substitution Principle (LSP) này. Nguyên tắc này cho rằng: trong kế thừa, các class con, class kế thừa phải luôn có thể thay thế được class cha. Tức là, nếu class A kế thừa từ class B, thì mình luôn có thể sử dụng class A thay cho class B mà các chức năng không bị thay đổi.

image 23 - quochung.cyou PTIT

Lấy ví dụ về hình vuông và hình chữ nhật

image 28 - quochung.cyou PTIT
image 25 - quochung.cyou PTIT

Như trong toán học được dạy ở các cấp dưới, ta hay được nghe là “hình vuông cũng là hình chữ nhật”, Nhìn ví dụ trên ta thấy mọi tính toán đều rất hợp lý. Do hình vuông có 2 cạnh bằng nhau, mỗi khi set độ dài 1 cạnh thì ta set luôn độ dài của cạnh còn lại bằng cách viết đè phương thức set chiều cao và set chiều rộng.

Tuy nhiên, class HinhVuong sau khi kế thừa class HinhChuNhat đã làm thay đổi các đặc tính vốn có của HinhChuNhat, dẫn đến vi phạm LSP. Thử với một ví dụ như sau

image 26 - quochung.cyou PTIT

Rõ ràng, lúc này ta khai báo một object class HinhChuNhat theo HinhVuong, set chiều cao và chiều rộng, nhưng do ta đã ghi đè hàm set chiều cao chiều rộng nên chiều cao chiều rộng bị cập nhật thành 10, và tính diện tích ra là 10×10 = 100, rõ ràng không đúng vì hình chữ nhật đúng ra diện tích là 5×10 = 50, hay có thể nói là: Class HinhVuong không thể dùng thay thế cho class HinhChuNhat

Những vi phạm về nguyên lý LSP

  • Các lớp dẫn xuất có các phương thức ghi đè phương thức của lớp cha nhưng với chức năng hoàn toàn khác.
  • Các lớp dẫn xuất có phương thức ghi đè phương thức của lớp cha là một phương thức rỗng.
  • Các phương thức bắt buộc kế thừa từ lớp cha ở lớp dẫn xuất nhưng không được sử dụng.
  • Phát sinh ngoại lệ trong phương thức của lớp dẫn xuất.
image 27 - quochung.cyou PTIT

Đây là nguyên lý… dễ bị vi phạm nhất, nguyên nhân chủ yếu là do sự thiếu kinh nghiệm khi thiết kế class. Thuông thường, design các class dựa theo đời thật: hình vuông là hình chữ nhật, file nào cũng là file. Tuy nhiên, không thể bê nguyên văn mối quan hệ này vào code. Hãy nhớ 1 điều:

  • Trong thực tế, A là B (hình vuông là hình chữ nhật) không có nghĩa là class A nên kế thừa class B. Chỉ cho class A kế thừa class B khi class A thay thế được cho class B.
  • Nguyên lý này ẩn giấu trong hầu hết mọi đoạn code, giúp cho code linh hoạt và ổn định mà ta không hề hay biết. Ví dụ như trong Java, ta có thể chạy hàm foreach với List, ArrayList, LinkedList bởi vì chúng cùng kế thừa interface Iterable. Các class List, ArrayList, … đã được thiết kế đúng LSP, chúng có thể thay thế cho Iterable mà không làm hỏng tính đúng đắn của chương trình.

Theo đó, để sửa vấn đề hình vuông – hình chữ nhật trên, ta nên để chúng cùng kế thừa một class Shape như sau

image 29 - quochung.cyou PTIT

Lúc này việc set các chiều cao và chiều rộng thì chỉ class con mới có, và không vi phạm nguyên tắc LSP.

Việc thiết kế áp dụng theo nguyên tắc LSP giúp chúng ta giảm bớt quá lạm dụng việc kế thừa trong class. Ý nghĩa của các chức năng không được thay đổi để có thể sử dụng ở nhiều phạm vi khác nhau hơn.

Nguyên tắc 4: Interface segregation principle – Nguyên tắc chia nhỏ

Nguyên tắc này được phát biểu như sau:

image 31 - quochung.cyou PTIT

Thay vì dùng 1 interface lớn, ta nên tách thành nhiều interface nhỏ, với nhiều mục đích cụ thể.

Many client-specific interfaces are better than one general-purpose interface.

Theo nguyên tắc ISP, một class con khi implement các interface thì không nên bị bắt buộc implement các phương thức mà mình không sử dụng bao giờ. Theo cách hiểu trên, nguyên tắc này sẽ ưu tiên việc chia nhỏ các interface ra thành các phương thức sử dụng cho các mục đích đặc thù hơn, tránh sử dụng cả một interface lớn.

Hay nói một cách khác, khi ta implement một interface trong 1 class, mà có 1 vài phương thức mình cứ phải kế thừa dù cũng không cần thiết không phải một cách hay. Ngắn gọn là: Không một client nào nên bị bắt buộc phải kế thừa một phương thức nào đó mà nó không dùng

Hãy tưởng tượng chúng ta có 1 interface lớn, khoảng 100 methods. Việc implements sẽ khá cực khổ, ngoài ra còn có thể dư thừa vì 1 class không cần dùng hết 100 method. Khi tách interface ra thành nhiều interface nhỏ, gồm các method liên quan tới nhau, việc implement và quản lý sẽ dễ hơn.

image 33 - quochung.cyou PTIT
image 30 - quochung.cyou PTIT

Như vậy, việc class Bike implement interface Vehicle khiến class Bike phải viết lại cả hàm openDoor() mở cửa, dù xe đạp thì không có cửa, và thường thì ta sẽ phải để nó rỗng. Như vậy khiến code bị dư thừa và nếu mở rộng ra thì các class con ngày càng bị phình to, khó bảo trì hơn

image 32 - quochung.cyou PTIT
  • Việc áp dụng nguyên tắc trên giúp code dễ đọc và dễ quản lí bảo trì hơn. Giảm bớt code dư thừa và chỉ phải viết các phương thức cần thiết.

Nguyên tắc 5: Dependency inversion principle – Nguyên tắc đảo ngược phụ thuộc

Lấy ví dụ:

image 35 - quochung.cyou PTIT

Như ta thấy, các thiết bị như tai nghe dùng cổng 3.5mm, điện thoại Android dùng dây sạc TypeC, điện thoại IOS dùng dây sạc Lightning, cổng cắm của Camera cũng khác, …

Vậy khi ta làm dây sạc, dây kết nối cho các máy này. Ta có chọn trước là nó sẽ dùng cho thiết bị nào không? Rõ ràng là không, các dây sạc này (các module cấp thấp) không quy định là nó dùng cho module cấp cao nào, mà chính các thiết bị như máy ảnh, điện thoại, tai nghe mới quy định nó dùng module cấp thấp nào.

Nguyên tắc này được phát biểu như sau:

  1. Các module cấp cao không nên phụ thuộc vào các modules cấp thấp. Cả 2 nên phụ thuộc vào abstraction.
  2. Interface (abstraction) không nên phụ thuộc vào chi tiết, mà ngược lại. ( Các class giao tiếp với nhau thông qua interface, không phải thông qua implementation.)
  1. High-level modules should not depend on low-level modules. Both should depend on abstractions.
  2. Abstractions should not depend upon details. Details should depend upon abstractions.
Ví dụ, ta đang thiết kế một hệ thống thanh toán bằng ngân hàng như sau:
image 39 - quochung.cyou PTIT

Ví dụ lúc này, ta cần thêm phương thức thanh toán bằng tiền mặt, cần có thêm một số tham số thì sao?
image 40 - quochung.cyou PTIT
image 41 - quochung.cyou PTIT

Ta thấy lúc này ta lại phải sửa đổi ở module cấp cao (highlevel module) là CuaHang, thêm if else để xử lí phương thức mới, mà sau này, nếu thêm các phương thức mới nữa, ta lại cứ phải thêm rất nhiều if else vào cái class lớn này.

Nó đã vi phạm 2 nguyên tắc là nguyên tắc (Nguyên tắc đảo ngược phụ thuộc) vì một class level cao hơn lại khai báo các chi tiết của class level bé hơn, hơn nữa lại còn sai về (Nguyên tắc một chức năng) vì đã khai báo nhiều chức năng hơn trong class lớn. Đúng ra class lớn chỉ nên thực hiện thanh toán thôi, còn cụ thể thanh toán thế nào thì phải là class khác.

Áp dụng nguyên tắc Dependency inversion principle – Nguyên tắc đảo ngược phụ thuộc
image 42 - quochung.cyou PTIT
image 43 - quochung.cyou PTIT

Lúc này các module cấp cao high-level là CuaHang đã có một interface ở giữa là PhuongThucThanhToan với các chức năng của các lớp cấp thấp (low-level module) như ThanhToanTienMat, ThanhToanNganHang. Ta đã đảo ngược sự phụ thuộc.

Như vậy, khi ta muốn thêm các lớp thanh toán khác như PayPal, Thẻ tín dụng, ví điện tử,…. Ta chỉ cần viết thêm các class con khác kế thừa từ interface PhuongThucThanhToan, chứ không cần động gì vào các code cũ này nữa.

image 44 - quochung.cyou PTIT
  • Code có thể tái sử dụng
  • Code dễ dàng quản lí hơn
  • Chia nhỏ các phần giúp việc test đơn giản hơn
  • Giảm bớt việc lỗi khi động vào các class cao hơn.

Tài liệu tham khảo:

  • https://gpcoder.com/4200-cac-nguyen-ly-thiet-ke-huong-doi-tuong/
  • https://www.interviewbit.com/blog/solid-principles-java/
  • https://stg-tud.github.io/sedc/Lecture/ws13-14/3.3-LSP.html#mode=document
  • https://www.codeproject.com/Articles/538536/A-curry-of-Dependency-Inversion-Principle-DIP-Inve

Tìm hiểu về Serverless dễ hiểu bằng trà sữa

1 kvYDcS71SZXcyP yeqQMww - quochung.cyou PTIT

Serverless là gì, IaaS, CaaS, FaaS là gì, ..

Trước tiên để tìm hiểu về cấu trúc Serverless, trước hết ta hãy xem thử các cấu trúc truyền thống thường được sử dụng, và một số từ khoá và định nghĩa của chúng.

Mô tả cấu trúc Monolithic truyền thống

Ví dụ với một cấu trúc như trên, toàn bộ ứng dụng của chúng ta sẽ được chứa chung trong 1 khối lớn, và cùng tương tác vào 1 database. Để dễ hiểu hơn, mình sẽ ví dụ chúng như sau:

Một cửa hàng bán trà sữa bán 4 loại trà sữa:

  • Trà sữa dâu
  • Trà sữa ô long
  • Trà sữa bạc hà
  • Trà sữa socola

Tất cả trà sữa này sẽ được nhân viên pha bằng cùng 1 chiếc máy, cái máy này có thể pha được cả 4 loại trà sữa trên, rất đa năng tiện dụng. Ngoài ra, để có thể pha trà sữa bằng máy, ta cần nhiên liệu pha trà sữa, các nhiên liệu cho 4 loại trà sữa thì khác nhau, và được để chung trong 1 thùng nhiên liệu.

  • Máy pha trà sữa – Code/Server xử lí Logic tính toán, ..
  • Thùng nhiên liệu – Database, cơ sở dữ liệu tổng
  • Nhiên liệu – Các dữ liệu

Vậy ta có thể thấy, với một lượng người dùng chưa lớn, xây dựng cấu trúc đưa toàn bộ các logic vào 1 server như trên có thể khả thi, tuy nhiên, khi người dùng ngày càng tăng lên, việc làm trà sữa nào cùng phải dùng chung 1 cái máy, và việc lấy nhiên liệu nào cũng phải lấy từ 1 thùng nhiên liệu lớn sẽ khiến cho 1 khối chung quá tải, một trong những cách tối ưu cho vấn đề này là chia nhỏ các công việc nhỏ ra.

Tối ưu: Chia nhỏ các vấn đề

Lúc này, ta sẽ chia nhỏ các vấn đề thành 4 luồng như sau, vẫn tại cùng 1 địa điểm, 1 server cửa hàng đó, ta chia cấu trúc thành:

  • Máy pha trà sữa 1, chỉ pha trà sữa dâu. Cùng 1 thùng nhiên liệu chỉ cho trà sữa dâu
  • Máy pha trà sữa 2, chỉ pha trà sữa ô long. Cùng 1 thùng nhiên liệu chỉ cho trà sữa ô long
  • Máy pha trà sữa 3, chỉ pha trà sữa bạc hà. Cùng 1 thùng nhiên liệu chỉ cho trà sữa bạc hà
  • Máy pha trà sữa 4, chỉ pha trà sữa socola. Cùng 1 thùng nhiên liệu chỉ cho trà sữa socola

Lúc này, với các khách order các trà sữa khác nhau, nhân viên có thể đến các máy pha và thùng nhiên liệu khác nhau, chia tách thành các luồng nên sẽ giảm tải hơn một chút. Tuy nhiên, lại có một số vấn đề như sau:

  • Các luồng sẽ không giống nhau. Ví dụ trà sữa dâu thường được gọi nhiều hơn trà sữa socola, nên đúng ra cái máy, hay tài nguyên server (cpu, ram,…) nên được phân bố nhiều hơn cho trà sữa socola
  • Nếu một máy trà sữa bị lỗi, ví dụ hãy tưởng tượng về code, dù lúc này đã chia nhỏ nhiều function ra, nhưng nếu hỏng cái gì thì thường ta phải khởi động lại cả server chung, đồng nghĩa các function khác đang chạy tốt cũng phải khởi động lại.

Lúc này, ta sẽ thử nói về một số từ khoá trong triển khai cấu trúc để xử lí vấn đề trên

image 1 - quochung.cyou PTIT

Lại tưởng tượng về cửa hàng bán trà sữa, hãy coi cả cửa hàng như một cái máy chủ, một cái máy tính để có thể chạy các code (các máy pha trà sữa). Chúng ta có thể mua một mặt bằng để đặt cửa hàng (mua đứt). Tương tự, ta có thể mua các server về nhà chúng ta, về công ty để tự triển khai, tự lắp đặt.

image 8 - quochung.cyou PTIT

Triển khai 1: Tự cài đặt server

Một server thì nhìn chung có cấu trúc tương tự như một máy tính, laptop bình thường. Tuy nhiên được thiết kế để chịu tải tốt hơn, chạy 24/7, chịu đựng các điều kiện như nhiệt độ, thời tiết, bụi, … tốt hơn, và một số đặc tính khác nữa.

image 2 - quochung.cyou PTIT

Việc tự mua một server về triển khai thường được các công ty lớn sử dụng, tuy nhiên lúc này ta sẽ phải lo các vấn đề như nhiệt độ, cần bật máy làm mát, điều hoà, quạt,… để server mát mẻ 24/7. Hay phải lo các vấn đề như điện để tải server, tránh các trường hợp mất điện thì server cũng hỏng, ….

Nó giống với việc bạn mua mặt bằng để làm cửa hàng trà sữa, sau khi mua xong thì không thể mở rộng đất ra nếu muốn mở rộng cửa hàng được, phải tự lo các vấn đề như vệ sinh mặt bằng, điện nước, …. Điều này không phù hợp nếu ta chỉ cần một dịch vụ nhỏ, hoặc muốn mở rộng lớn ra hơn sau này.

Tối ưu: Đưa các dịch vụ lên các nền tảng riêng

Triển khai 2: infrastructure as a service (IaaS) – Cơ sở hạ tầng như một dịch vụ

image 3 - quochung.cyou PTIT

Như tên của nó, cơ sở hạ tầng như một dịch vụ. Tức là ta sẽ thuê luôn một mặt bằng cho cửa hàng trà sữa, một cái máy chủ về để sử dụng. Các đơn vị cho thuê thường là các công ty lớn như nước ngoài có Amazon, Microsoft, Google. Trong nước thì có Viettel Cloud, … Các nhà cho thuê, đơn vị này sẽ bảo đảm vấn đề điện duy trì server ổn định, băng thông mạng cho server tốc độ cao, ….

Ngoài ra nếu server các bạn bị tấn công, ddos, các đơn vị này cũng sẽ có thể chuyển bạn sang một server, cụm network mạng khác để tránh, …

Nhìn chung việc cho thuê hay mua dịch vụ này đã giảm tải bớt rất nhiều sự đau đầu cho bạn. Lúc này bạn như có một cái máy tính ảo có thể truy cập từ xa, và đặt các code của mình lên đây để chạy.

Tối ưu: Các dịch vụ tự co giãn theo nhu cầu, tiết kiệm chi phí

Triển khai 3: Container as a Service (CaaS) – Một khối container như dịch vụ

image 7 - quochung.cyou PTIT

Để giải thích việc này thì khá trừu tượng, nhưng lúc này bạn sẽ được cung cấp dịch vụ như các container. Các container này thì gọn nhẹ hơn, tốn ít tài nguyên, tiền bạc để thuê, và có thể co giãn được (tức là có thể tự tăng ram, cpu khi nhu cầu nâng cao)

Bạn tưởng tượng thì nếu bạn chia nhỏ các phần của ứng dụng của mình đủ nhỏ, để chúng có thể triển khai độc lập trên 1 container nhỏ, lúc này các hệ thống container có thể tạo ra rất nhiều bản container của phần ứng dụng đó của bạn, tự nâng cấu hình khi nhu cầu sử dụng tăng cao, hoặc tự triển khai một container khác chạy thay thế nếu container hiện tại bị sập, …

image 4 - quochung.cyou PTIT

Triển khai 4: Function as a Service (FaaS) – Một hàm, chức năng như một dịch vụ

Với triết lý lập trình hàm (Function do only one thing), khi làm việc với kiến trúc Functions as a service, chỉ nên để function làm một việc duy nhất

Điểm mạnh nhất của FaaS là nó độc lập các function, nếu function chỉ thực thi một nhiệm vụ duy nhất. FaaS sẽ làm rất tốt công việc của nó được giao. Tuy nhiên, nếu gọi các function lồng nhau, rất dễ xảy ra vấn đề về performance.

Tức là lúc này bạn có thể chia các máy bán trà sữa này làm một tác vụ duy nhất như 1 hàm, trong đó có cả thùng nhiên liệu là database nhỏ của riêng loại trà sữa đó thôi. Lúc này các máy bán trà sữa này được đặt tại nhiều điểm khác nhau, (bạn chỉ đang thuê cái máy – một function như một dịch vụ). Do kết nối internet hạ tầng đủ nhanh, chúng vẫn có thể chấp nhận được với cửa hàng trà sữa của bạn. Và nếu 1 máy hỏng thì các máy khác vẫn không làm sao cả.

Các FaaS cũng có thể có các tính năng kế thừa như CaaS, tức là tự tạo một điểm khác nếu quá tải, tự tạo ra nhiều điểm vẫn là cùng 1 máy bán trà sữa và chia nhỏ luồng hơn nữa để xử lý tốt hơn, hay tự khởi động một điểm khác khi chỗ hiện tại bị sập, …

image 5 - quochung.cyou PTIT

Một số từ khoá

Auto-scaling: Các máy chủ nhỏ này sẽ tự động co giãn ssd, ram, cpu, …. để phù hợp với nhu cầu, ví dụ máy pha trà sữa này nhiều khách order thì tự tăng tài nguyên lên, còn nếu ít khách thì tự giảm xuống

Event-driven: Các máy pha trà sữa nếu không ai gọi thì tự tắt đi, và chỉ bật lên (cold-start), sẽ bật lên (đủ nhanh tuỳ theo độ nặng của code bạn) khi có khách gọi (một event – sự kiện)

pay-per-execution: Một số chỗ sẽ thu phí bạn thuê theo số lần gọi dịch vụ chứ không phải thời gian thuê. ví dụ máy bán trà sữa dâu khá ế, 1 tháng có đúng 4 khách gọi 4 thời điểm khác nhau, bạn không cần bật máy cả tháng, mà chỉ tính tiền thuê 4 lần đó thôi

pay-as-use: Một số chỗ thu phí theo thời gian, tuy nhiên nhỏ lẻ hơn. Kiểu theo tiếng, theo ngày, … khi đó nếu bạn chỉ cần dùng 1 thời gian ngắn rồi tắt đi thì sẽ tiết kiệm chi phí hơn nhiều

Tóm lại về serverless

image 6 - quochung.cyou PTIT

Hi vọng qua ví dụ trà sữa trên bạn sẽ có một cái nhìn tổng quan hoặc cơ bản về serverless.

Serverless – Không có server, không có nghĩa là sẽ không có cái server máy chủ nào chạy code cả. Mà cơ bản bạn sẽ không cần sở hữu một cái máy chủ, tự bảo quản và vận hành nó, mà có thể thuê các đơn vị khác đảm bảo dịch vụ và ổn định hơn để thực thi các tác vụ của mình.

Như lúc này cửa hàng trà sữa có thể đưa 4 máy pha trà sữa, 4 đoạn code độc lập này lên 4 điểm khác nhau. Lúc này ví dụ 1 điểm code bị bug, có vấn đề thì các nơi khác vẫn hoạt động bình thường.

Ngoài ra các điểm này có thể tự co giãn – (tham khảo thêm ở phần từ khoá bên trên) để phục vụ tốt hơn nếu có nhu cầu nhiều hơn ở một luồng nào đó.

image 9 - quochung.cyou PTIT

Ưu điểm của serverless

  • Tiết kiệm chi phí, không cần thuê/mua cả mặt bằng – cả 1 server để chứa mấy cái máy pha, mà chỉ cần thuê cái máy pha là được
  • Giảm bớt nỗi lo quản lý mặt bằng – server như điện, tiền mạng, ….

Nhược điểm của serverless hay FaaS

  • Test và debug khó hơn, do nó chỉ bật khi mình cần, hơn nữa bạn không nhìn được rõ chúng đang chạy như thế nào như code trên máy mình, vì lúc này toàn bộ code đang ở một điểm khác.
  • Vấn đề bảo mật, lúc này bạn đang để database và code ở một đơn vị khác cho thuê. Nhìn chung thì chắc chắn việc bảo mật không thể bằng tự mình quản lí được.
  • Không phù hợp cho các tác vụ chạy liên tục 24/7. Do điểm tốt của serverless là tiết kiệm tiền, tự tắt đi khi mà không cần sử dụng. Nhưng nếu bạn cần dùng liên tục thì nên thuê CaaS hoặc IaaS
  • Giảm hiệu năng. Do các máy pha chỉ bật khi ta cần đến, nên sẽ tốn thêm thời gian bật lên. Bạn cần chia nhỏ function nhất có thể để chúng độc lập, và bật đủ nhanh để không ảnh hưởng người dùng

Cài đặt WSL2 Ubuntu trên Window ít lỗi không cần Microsoft Store

WSL là gì

tich hop nhan linux tren windows 10 1 - quochung.cyou PTIT

WSL (Windows Subsystem for Linux) là một tính năng có trên Windows x64 (từ Windows 10, bản 1607 và trên Windows Server 2019), nó cho phép chạy hệ điều hành Linux (GNU/Linux) trên Windows. Với WSL bạn có thể chạy các lệnh, các ứng dụng trực tiếp từ dòng lệnh Windows mà không phải bận tâm về việc tạo / quản lý máy ảo như trước đây. Cụ thể, một số lưu ý mà Microsoft liệt kê có thể làm với WSL:

  • Chọn sử dụng distro Linux từ Microsoft Store: Hiện giờ đang có các Distro Linux rất gọn nhẹ trên Store sử dụng được ngày như Ubuntu, Debian …
  • Chạy được từ dòng lệnh các lệnh linux như ls, grep, sed … hoặc bất kỳ chương trình nhị phân 64 bit (ELF-64) nào của Linux
  • Chạy được các công cụ như: vim, emacs …; các ngôn ngữ lập trình như NodeJS, JavaScript, C/C++, C# …, các dịch vụ như MySQL, Apache, lighthttpd …
  • Có thể thực hiện cài đặt các gói từ trình quản lý gói của Distro đó (như lệnh apt trên Ubuntu)
  • Từ Windows có thể chạy các ứng dụng Linux (dòng lệnh)
  • Từ Linux có thể gọi ứng dụng của Windows

Một số phần cần dựa vào WSL để chạy trên Win

  • Docker
  • Terminal (Konsole)
  • ZSH / Fish
  • VSCode
  • Git
  • PHP
  • Node.js
  • Golang

Cài đặt WSL2 Ubuntu không cần sử dụng Microsoft Store, tránh một số lỗi như Window tắt Update, …

Đầu tiên, bật Powershell lên, có thể dùng Window + S để tìm powershell

image - quochung.cyou PTIT


# Tải về Ubuntu 20.04, copy lệnh dưới vào powershell
Invoke-WebRequest -Uri https://aka.ms/wslubuntu2004 -OutFile Ubuntu.appx -UseBasicParsing

# Cài đặt file vừa tải xong, copy lệnh dưới vào powershell
Add-AppxPackage .\Ubuntu.appx

Sau khi thấy đã cài đặt hoàn tất, Window + S và tìm ubuntu để chạy các phần setup cơ bản

Sau khi hoàn thiện, bật powershell lần nữa và chạy thử lệnh sau để kiểm tra ubuntu đã lên chưa

wsl --list --verbose

//Sẽ in ra dạng
  NAME                   STATE           VERSION
  Ubuntu-20.04           Running         1
maxresdefault 1 - quochung.cyou PTIT

Nếu đã hiển thị như trên, gõ lệnh sau để sang WSL2

wsl --set-version Ubuntu-20.04 2

[Web-Buổi 1] Tìm hiểu về cách hoạt động của Web, HTML cơ bản

Mục lục:

  • Cách thức hoạt động của một trang web
  • Cấu trúc của HTML
  • Block, inline elements. Qua phân biệt div, span.
  • Các thẻ liên quan đến table trong HTML
  • Các thẻ: image, video, links, audio
  • Section elements: div, span, header, footer, nav, main, section, article
  • HTML text fundamentals: h1 -> h6, p, ul, li
  • Các thẻ liên quan đến form: form, input, button, label, textarea
  • Có thể tìm hiểu các thẻ khác ngoài các thẻ trên các thẻ trên

I. Cách thức hoạt động của một trang web

Khi máy tính nhà của mình kết nối đến một trang web trên mạng, ta có thể gọi chúng là các clients (máy khách) và servers (máy chủ).

Ảnh minh hoạ: ảnh minh hoạ 1

Client máy khách sẽ gửi các Requests (yêu cầu) về máy chủ, và máy chủ sẽ trả về Response (Phản hồi) tương ứng với yêu cầu của người dùng.

VD:

  • Người dùng gửi yêu cầu tìm trang bạn bè, server trả về trang banbe.html
  • Người dùng gửi yêu cầu nhắn tin, server trả về dữ liệu tin nhắn

Ở thời kì đầu của web, đa phần các web đều là web tĩnh. Tức là các trang web chỉ có thể hiển thị các nội dung cố định, không thể thay đổi được. Ví dụ như các trang web tin tức, các trang web thông tin, các trang web giới thiệu sản phẩm, …

Tuy nhiên, với sự phát triển của công nghệ, các trang web ngày nay có thể thay đổi được nội dung, có thể tương tác với người dùng. Ví dụ như các trang web mạng xã hội, các trang web thương mại điện tử, các trang web chơi game, … (Được gọi là web động). Các trang web động sẽ có dữ liệu hiển thị tuỳ theo người dùng khác nhau, và có thể thay đổi được dữ liệu hiển thị tuỳ theo người dùng. Ví dụ như trang web mạng xã hội, khi bạn đăng nhập vào trang web, bạn sẽ thấy các bài viết của bạn bè, và bạn có thể đăng bài viết của mình lên trang web.

ảnh minh hoạ 2

Vậy nhìn chung thì Web là một dữ liệu sẽ được server trả về khi người dùng yêu cầu. Tuy nhiên nếu đào sâu ra thì đây là một quá trình rất dài, tưởng tượng máy bạn là nhà bạn, và web bạn truy cập là một cửa hàng trên phố, lúc này để truy cập web, bạn cần đi qua một tuyến đường để đến cửa hàng đó.

Quá trình xảy ra khi bạn kết nối một trang web:

  • Đầu tiên tưởng tượng, để đến một cửa hàng (web), ta cần địa chỉ của nó. Các máy chủ hay máy người dùng khi kết nối đến hệ thống internet toàn cầu đều được cấp một địa chỉ, thường là Ipv4 (ví dụ 98.139.180.149)
  • Tuy nhiên, số lượng ipv4 là có hạn. Vì vậy thông thường các máy tính sẽ nằm trong một mạng cục bộ, và chỉ có một ipv4 bên ngoài quyết định chúng. VD: Toàn bộ máy tính của 1 toà nhà sẽ đều kết nối vào một modem mạng có Ipv4 là (98.139.180.149). Đây sẽ là địa chỉ để bên ngoài có thể kết nối tới. Tuy nhiên khi có yêu cầu gửi tới, ta có thể cài đặt nó sẽ được đi vào máy tính nào trong toà nhà đó, các máy tính lúc này sẽ có một ipv4 cục bộ kiểu: 192.168.0.1, 192.168.0.2, ….. Tưởng tượng: Các cửa hàng nằm trong phố Nguyễn Trãi, ta đi đến Nguyễn Trãi và nó sẽ là số 100 Nguyễn Trãi, số 101 Nguyễn Trãi, …
ảnh minh hoạ 3
  • Như vậy ta đã biết là bằng cách trên. Một server có thể truy cập bằng một địa chỉ Ipv4. Kiểu ta có thể truy cập bằng http://98.139.180.149. Tuy nhiên người dùng không thể nhớ hết số IP này, vì vậy ta cần một hệ thống định dang (tên miền – domain) để biến ip số này thành các tên miền dễ nhớ. VD: http://google.com, http://facebook.com, http://youtube.com, …
  • Để có thể biết được địa chỉ ip của một tên miền, ta cần một hệ thống DNS (Domain Name System). Hệ thống này sẽ lưu trữ các tên miền và địa chỉ ip tương ứng của nó. VD: google.com -> 20.002.003.149
  • Các hệ thống DNS như một cuốn từ điển và cho biến tên miền nào chỉ đến ip nào. Thông thường khi kết nối, đầu tiên máy tính sẽ tìm lại vào cache của chính máy mình để xem tên miền đã được lưu chưa, nếu chưa có thì máy tính sẽ tìm đến các dns server của ISP (nhà mạng, ví dụ Viettel, VNPT). Nếu vẫn chưa tìm thấy, máy tính sẽ tìm đến các DNS Server khác như của Google (8.8.8.8), Cloudflare (1.1.1.1), …
  • Khi người dùng gõ tên miền vào trình duyệt, trình duyệt sẽ gửi yêu cầu tới DNS để lấy địa chỉ ip của tên miền đó. Sau đó trình duyệt sẽ gửi yêu cầu tới địa chỉ ip đó, và server sẽ trả về dữ liệu tương ứng với yêu cầu của người dùng.
ảnh minh hoạ 4

II. Cấu trúc của HTML

ảnh minh hoạ 5
ảnh minh hoạ 6
  • HTML là một ngôn ngữ đánh dấu siêu văn bản (HyperText Markup Language). Nó được sử dụng để xây dựng các trang web. HTML được sử dụng để đánh dấu các phần tử trong một trang web. Các phần tử này được đánh dấu bằng các thẻ HTML. Mỗi thẻ HTML bắt đầu bằng một ký tự đặc biệt và kết thúc bằng một ký tự đặc biệt. Nội dung giữa các thẻ HTML là nội dung của phần tử đó. Ví dụ: <p> This is a paragraph </p>
  • Các thẻ HTML có thể được phân thành hai loại: thẻ đóng và thẻ không đóng. Thẻ đóng có một thẻ đóng tương ứng, trong khi thẻ không đóng không có thẻ đóng tương ứng. Ví dụ: <p> This is a paragraph </p> là một thẻ đóng, trong khi <br> là một thẻ không đóng.

III. Thẻ khối và thẻ nội tuyến

Các thẻ HTML có thể được nhóm thành các thẻ khối và các thẻ nội tuyến. Các thẻ khối bắt đầu trên một dòng mới và chiếm toàn bộ chiều rộng của trình duyệt, trong khi các thẻ nội tuyến không bắt đầu trên một dòng mới và chỉ chiếm một phần của chiều rộng của trình duyệt. Ví dụ: <p> là một thẻ khối, trong khi <b> là một thẻ nội tuyến.

Có 2 kiểu giá trị hiển thị chính : block và inline.

Block-level elements (thẻ khối) :

Một block-elements luôn bắt đầu trên một dòng mới và Browser tự động thêm khoảng trắng (margin) và trước và sau phần tử đó. Nó luôn chiếm toàn bộ chiều rộng có sẵn. 2 block-elements thường dùng là : <p> và <div>.

Inline elements (thẻ nội tuyến) :

Một Inline elements không bắt đầu trên một dòng mới. Nó chỉ chiếm chiều rộng cần thiết. Một phần tử inline không thể chưa phần tử block.

  • <div> là một thẻ khối, nó được sử dụng để nhóm các phần tử HTML lại với nhau. Nó không có bất kỳ ý nghĩa đặc biệt nào. Nó thường được sử dụng để tạo các nhóm phần tử CSS để áp dụng các kiểu CSS cho các phần tử đó. Ví dụ: <div> This is a div </div>
  • <span> là một thẻ nội tuyến, nó được sử dụng để nhóm các phần tử HTML lại với nhau. Nó không có bất kỳ ý nghĩa đặc biệt nào. Nó thường được sử dụng để tạo các nhóm phần tử CSS để áp dụng các kiểu CSS cho các phần tử đó. Ví dụ: <span> This is a span </span>

IV. Các thẻ liên quan đến table trong HTML

  • <table> là một thẻ khối, nó được sử dụng để tạo một bảng. Nó có thể chứa một hoặc nhiều thẻ <tr>. Ví dụ: <table> This is a table </table>
  • <tr> là một thẻ khối, nó được sử dụng để tạo một hàng trong bảng. Nó có thể chứa một hoặc nhiều thẻ <td>. Ví dụ: <tr> This is a table row </tr>
  • <td> là một thẻ nội tuyến, nó được sử dụng để tạo một ô trong bảng. Nó có thể chứa một hoặc nhiều thẻ <p>. Ví dụ: <td> This is a table cell </td>
  • <th> là một thẻ nội tuyến, nó được sử dụng để tạo một ô trong bảng. Nó có thể chứa một hoặc nhiều thẻ <p>. Nó khác với thẻ <td> ở chỗ nó được sử dụng để tạo một ô tiêu đề trong bảng. Ví dụ: <th> This is a table header </th>
ảnh minh hoạ 7

  • <img> là một thẻ nội tuyến, nó được sử dụng để chèn một hình ảnh vào trang web. Nó không có thẻ đóng tương ứng. Ví dụ: <img src="image.jpg" alt="Image">
<img src="url" alt="alternatetext">
  • <video> là một thẻ khối, nó được sử dụng để chèn một video vào trang web. Nó có thể chứa một hoặc nhiều thẻ <source>. Ví dụ: <video> This is a video </video>
  • <source> là một thẻ nội tuyến, nó được sử dụng để chỉ định một nguồn video. Nó không có thẻ đóng tương ứng. Ví dụ: <source src="video.mp4" type="video/mp4">
  • <audio> là một thẻ khối, nó được sử dụng để chèn một âm thanh vào trang web. Nó có thể chứa một hoặc nhiều thẻ <source>. Ví dụ: <audio> This is an audio </audio>
  • <source> là một thẻ nội tuyến, nó được sử dụng để chỉ định một nguồn âm thanh. Nó không có thẻ đóng tương ứng. Ví dụ: <source src="audio.mp3" type="audio/mp3">
  • <a> là một thẻ nội tuyến, nó được sử dụng để tạo một liên kết đến một trang web khác. Nó có thể chứa một hoặc nhiều thẻ <img>. Ví dụ: <a href="https://www.google.com"> This is a link </a>
<a href="url">link text</a>
  • attributes quan trọng nhất của thẻ là “href”, nó cho biết đích đến của link.
  • Target attribute :
    • _self : mở tài liệu cùng cửa sổ khi nhấn link.
    • _blank : mở tài liệu ở cửa sổ mới.
    • _parent : mở tài liệu trong khung chính.
    • _top : mở tài liệu trong toàn bộ cửa sổ.

VI. Section elements: div, span, header, footer, nav, main, section, article

https://www.w3schools.com/html/img_sem_elements.gif
  • <div> là một thẻ khối, nó được sử dụng để nhóm các phần tử HTML lại với nhau. Nó không có bất kỳ ý nghĩa đặc biệt nào. Nó thường được sử dụng để tạo các nhóm phần tử CSS để áp dụng các kiểu CSS cho các phần tử đó. Ví dụ: <div> This is a div </div>
  • <span> là một thẻ nội tuyến, nó được sử dụng để nhóm các phần tử HTML lại với nhau. Nó không có bất kỳ ý nghĩa đặc biệt nào. Nó thường được sử dụng để tạo các nhóm phần tử CSS để áp dụng các kiểu CSS cho các phần tử đó. Ví dụ: <span> This is a span </span>
  • <header> là một thẻ khối, nó được sử dụng để tạo một tiêu đề cho trang web hoặc một phần của trang web. Ví dụ: <header> This is a header </header>
  • <footer> là một thẻ khối, nó được sử dụng để tạo một chân trang cho trang web hoặc một phần của trang web. Ví dụ: <footer> This is a footer </footer>
  • <nav> là một thẻ khối, nó được sử dụng để tạo một thanh điều hướng cho trang web hoặc một phần của trang web. Ví dụ: <nav> This is a nav </nav>
  • <main> là một thẻ khối, nó được sử dụng để tạo một phần chính của trang web. Ví dụ: <main> This is a main </main>
  • <section> là một thẻ khối, nó được sử dụng để tạo một phần của trang web. Ví dụ: <section> This is a section </section>
  • <article> là một thẻ khối, nó được sử dụng để tạo một bài viết. Ví dụ: <article> This is an article </article>

VII. HTML text fundamentals: h1 -> h6, p, ul, li

ảnh minh hoạ 8
  • <h1> là một thẻ khối, nó được sử dụng để tạo một tiêu đề lớn nhất cho trang web hoặc một phần của trang web. Ví dụ: <h1> This is a heading </h1>
  • <h2> là một thẻ khối, nó được sử dụng để tạo một tiêu đề lớn hơn <h1> cho trang web hoặc một phần của trang web. Ví dụ: <h2> This is a heading </h2>
  • <p> là một thẻ khối, nó được sử dụng để tạo một đoạn văn bản. Ví dụ: <p> This is a paragraph </p>
  • <ul> là một thẻ khối, nó được sử dụng để tạo một danh sách không có thứ tự. Nó có thể chứa một hoặc nhiều thẻ <li>. Ví dụ: <ul> This is an unordered list </ul>
  • <li> là một thẻ khối, nó được sử dụng để tạo một mục trong danh sách. Nó có thể chứa một hoặc nhiều thẻ <p>. Ví dụ: <li> This is a list item </li>
  • <ol> là một thẻ khối, nó được sử dụng để tạo một danh sách có thứ tự. Nó có thể chứa một hoặc nhiều thẻ <li>. Ví dụ: <ol> This is an ordered list </ol>
ảnh minh hoạ 9

VIII. Các thẻ liên quan đến form: form, input, button, label, textarea

  • <form> là một thẻ khối, nó được sử dụng để tạo một biểu mẫu. Nó có thể chứa một hoặc nhiều thẻ <input><button><label><textarea>. Ví dụ: <form> This is a form </form>
  • <input> là một thẻ nội tuyến, nó được sử dụng để tạo một trường nhập liệu. Nó không có thẻ đóng tương ứng. Ví dụ: <input type="text">
  • <button> là một thẻ nội tuyến, nó được sử dụng để tạo một nút. Nó có thể chứa một hoặc nhiều thẻ <img>. Ví dụ: <button> This is a button </button>
  • <label> là một thẻ nội tuyến, nó được sử dụng để tạo một nhãn cho một trường nhập liệu. Nó có thể chứa một hoặc nhiều thẻ <input>. Ví dụ: <label> This is a label </label>
  • <textarea> là một thẻ khối, nó được sử dụng để tạo một trường nhập liệu dài. Nó không có thẻ đóng tương ứng. Ví dụ: <textarea> This is a textarea </textarea>
  • <select> là một thẻ khối, nó được sử dụng để tạo một trường nhập liệu dạng danh sách thả xuống. Nó có thể chứa một hoặc nhiều thẻ <option>. Ví dụ: <select> This is a select </select>
  • <option> là một thẻ khối, nó được sử dụng để tạo một mục trong danh sách thả xuống. Nó không có thẻ đóng tương ứng. Ví dụ: <option> This is an option </option>
ảnh minh hoạ 11

IX. Có thể tìm hiểu các thẻ khác ngoài các thẻ trên các thẻ trên

  • <br> : Xuống dòng
  • <hr> : Thẻ ngang
  • <i> : In nghiêng
  • <b> : In đậm

Review VPS tại Hosthatch.com, VPS chất lượng tốt giá rẻ gần Việt Nam

Link trải nghiệm

Sau khi trải nghiệm nhiều dịch vụ VPS có location ở gần Việt Nam, mình thấy đa số giá cả khá đắt, không phù hợp với budget sinh viên của mình lắm. Sau một thời gian tìm hiểu, và xem đánh giá tại https://www.vpsbenchmarks.com/, mình có tìm thấy một bên mới có giá cả khá oke với location Hongkong là Hosthatch

image 5 - quochung.cyou PTIT

Giá cả phải chăng cho chất lượng

Hosthatch có cả server Sing, Tokyo ngoài HongKong.

image 8 - quochung.cyou PTIT

Với mức giá khá phải chăng, khoảng 270-280k cho gói NVME 12G ram, 4 Core và 50GB NVMe (Không phải ssd nhé). Với mức giá này ở một số bên tại Việt Nam như Ftech, Vietnix chỉ có khoảng 2G ram, 1 core với cấu hình tương tự.

VPS hiếm hoi có location ở Hong Kong

Như bạn đã biết location Hong Kong rất gần Việt Nam. Do vậy không có gì ngạc nhiên khi website lưu trữ ở vị trí này có tốc độ truy cập từ Việt Nam rất tốt. Thậm chí không kém gì với location trong nước.

Một lợi thế khác của Location Hong Kong: 

Tính ổn định cao khi đứt cáp xảy ra.

Khi sử dụng dịch vụ hosting của các nhà cung cấp nước ngoài, bạn luôn lo lắng về đứt cáp xảy ra phải không?

Location Hong Kong thường ít bị ảnh hưởng bởi sự cố đứt cáp. Location Nhật Bản, Singapore thường bị ảnh hưởng nhiều hơn. 

image 6 - quochung.cyou PTIT

​Setup sử dụng, cài đặt VNC Server để điều khiển như Remote Desktop

hosthatch console - quochung.cyou PTIT
Hosthatch cung cấp một trang điều khiển dạng như trên

Có vài option hdh sẵn để lựa chọn, đa số là các bản phổ biến như Ubuntu, Debian, ….

Hosthatch sẽ cung cấp root password, sau đó bạn có thể ssh vào vps theo lệnh

ssh root + @ + ipaddress

Thông thường mình sẽ cài luôn vnc lên vps để tiện điều khiển, nếu muốn một giao diện để sử dụng VPS cho dễ, các bạn có thể làm theo các bước sau sau khi ssh

Cài đặt Desktop và VNC Server

Desktop: xfce4

sudo apt update 
sudo apt install xfce4 xfce4-goodies
sudo apt install tightvncserver
vncserver

Lúc này bạn sẽ được hỏi đặt mật khẩu, bạn tạo mật khẩu nữa là xongật

Output
You will require a password to access your desktops.

Password:
Verify:

Mật khẩu chỉ được dài 6-8 kí tự, nếu bạn nhập dài hơn nó sẽ tự động cắt ngắn

Output
Would you like to enter a view-only password (y/n)? n
xauth:  file /home/sammy/.Xauthority does not exist

New 'X' desktop is your_hostname:1

Creating default startup script /home/sammy/.vnc/xstartup
Starting applications specified in /home/sammy/.vnc/xstartup
Log file is /home/sammy/.vnc/your_hostname:1.log

Sau khi xong sẽ có thông báo dạng như sau

Bây giờ bạn có thể dùng một số phần mềm như https://www.realvnc.com/en/connect/download/viewer/ để kết nối với IP:5901 và sử dụng

xfce vnc - quochung.cyou PTIT

Đánh giá chung

Ưu điểm

  • Giá cả phải chăng, rẻ so với thị trường. Cấu hình cũng khá mạnh tương xứng
  • Có nhiều server gần Việt Nam như Hongkong, Sing với đường truyền tốt
  • Cài đặt nhanh, dễ dùng, ít downtime

Nhược điểm

  • Ít được biết đến, lúc mới mua xong mình loay hoay mãi mới cài được các thứ. Trong khi ở bên khác thì cài đặt và làm tiện nhanh hơn
  • Customer Support khá vô dụng, nhắn kĩ thuật có khi 1 tuần mới rep. Và theo nhiều review thì nếu nhắn quá gắt có thể bị khoá tài khoản luôn

Tìm hiểu về Java Swing

  1. Java Swing
  1. Định Nghĩa
  • Swing trong Java là một bộ công cụ Giao diện Người dùng Đồ họa (GUI) bao gồm các thành phần GUI. Swing cung cấp một bộ widget và gói phong phú để tạo ra các thành phần GUI tinh vi cho các ứng dụng Java. Swing là một phần của Java Foundation Classes (JFC), là một API để lập trình Java GUI cung cấp GUI.
  • Thư viện Java Swing được xây dựng dựa trên Bộ công cụ tiện ích con trừu tượng Java ( AWT ), một bộ công cụ GUI phụ thuộc vào nền tảng cũ hơn. Bạn có thể sử dụng các thành phần lập trình GUI đơn giản của Java như nút, hộp văn bản, v.v., từ thư viện và không phải tạo các thành phần từ đầu. Tuy nhiên, nó lại khác với AWT ở chỗ bộ công cụ này thuộc loại nền tảng độc lập, bao gồm các thành phần nhẹ và phức tạp hơn AWT.
  • Gói javax.swing cung cấp các lớp cho java swing API như JButton, JTextField, JTextArea, JRadioButton, JCheckbox, JMenu, JColorChooser, etc..
  1. Sử dụng
  • Java Swing được dùng để hỗ trợ tạo giao diện đồ hoạ người dùng (với Java).
  • Bộ công cụ này cung cấp các bộ điều khiển nâng cao như thanh trượt, colorpicker, Tree, TabbedPane và bảng điều khiển,..
  • Swing có những đặc điểm:
    • Độc lập với thiết bị.
    • Có thể tuỳ chỉnh, mở rộng.
    • Khá nhẹ.
    • Có thể cấu hình.

Các lớp và phân tầng Java Swing

EuTjh3a 8PoWdTXJVM4T2SEgYdJ1TqZVyiul2rMB 43nq 2PRiquBGzFlJd7sY yrMfU QK4oV37CjX16dP8xoG4Vu80hbre9sEWncuHmQU 7ji6LV ciPzBeM8PCz07txDiiaj6 D6 - quochung.cyou PTIT
  1. Các phương thức thường gặp ở lớp Component
Phương thứcMô tả
public void add(Component c)thêm một thành phần vào thành phần khác.
public void setSize(int width, int height)thiết lập kích thước của thành phần.
public void setLayout(LayoutManager m)thiết lập trình quản lý bố cục (layout) cho thành phần.
public void setVisible(boolean b)thiết lập khả năng hiển thị của thành phần. Nó theo mặc định là false (ẩn)
  1. Một số thành phần cơ bản
  1. JButton – Nút bấm
  • Nó được dùng để tạo ra một nút (button) có tên.
  • Việc sử dụng ActionListener sẽ dẫn đến một số hành động khi nút được nhấn.
  • Nó kế thừa lớp AbstractButton và độc lập với nền tảng.
I3sqx7ZizTt9ffnn0TVuacA2U1IMvRKhl ElXMr7fufQX6mu5fvImqgoTcK yGrHhvabR VBjDAL33B9qfE5r3ljqII676mn4RggTzYc0yRp - quochung.cyou PTIT
aKLIgPeLYlZzwA7WHFkLFOAO0uevX9mTYj237Ekjv18xPM QXIT0GBjt1FG9kBwQMQ ZO30H O2xehKKJvdX7rrNxuAxZV1ivVLkMU6uZ8o - quochung.cyou PTIT
  1. JTextField – Ô nhập
  • Nó kế thừa lớp JTextComponent và dùng để cho phép chỉnh sửa dòng đơn.
lQifr0kdtWBN8HY8S2cEUUWgKSOgr2 UyjfM6 rwmB6FThsklWGYS4xTBlmj kF9Vo3qXzl3 - quochung.cyou PTIT
UQScS xfvadwIljc0eawqsj3K5ME8upslkJRu7bXWWr6r5oWNSsh65XdgiRfulyu7jcIEWvNTrmLGzuHb1Q6V1qQf Q5fDshLF - quochung.cyou PTIT
  1. JScrollBar – Thanh cuộn
stM3 A6A04IhjAcpgFUJ37DPtzgFBRwHa gRpGtsKdVWt0b pHRF8ROR PdUsGUH ch0P2jyd91vR5eLBLgKEshklJG9dQge h3mgLGHIQBRn9wamYJF5cyI7mEimfqKjbTPiUdfEkNC - quochung.cyou PTIT
6zUcf5EZ0 kVtwUwUtHhMkTJRJMTDT8YQ7suA Ll6NDfHGOF0w4KcSjm4 - quochung.cyou PTIT
  1. JPanel – Thành phần chứa
  • Kế thừa lớp JComponent, cung cấp không gian cho một ứng dụng (có thể đính kèm bất kỳ thành phần nào khác).
QDeyI0HLhpiJQ0e87sttI34thu3F9DQH06 B2139HWnYjyGcMNx7XphboGbigmj8VAzkpRfOVsfV3QinM3oHmoBPlvPQzrayNw3AHzll6OnCFkHf67DaGl5uufA0 Q4IM8HWT0qlDZ5a - quochung.cyou PTIT
N b7R WACr JXwBGSqHd5KQOM8 s FCWysliktl1riOaEAiEBalFrlYYnkA7PEi r ZEL XL7M6 ff8f dNR6bLLDWu4PIVtVbmA8 TxLcRzFHUv K1iSfbmpw7PJFmhWwctCQuDQz5u - quochung.cyou PTIT
  • JMenu – Tạo menu
- quochung.cyou PTIT
Ac 89GgO1kGNni6rmV8Xw s7i2z5EPpIT34gEaELgDEnF0M5zolv7Xw 2hm9KWnS3q2 Ie1mm2Hm GNA3I4SSZGt61ikpCevEPLk8QRgO8VW At5DcRbL4wpoH1zpdYPkPRSvHorje - quochung.cyou PTIT
  • JList – Chứa danh sách
0pZpCe72qd7ylCDwqw5AW8OF37 p89nB0SoN74C9V6G6j02tsNvSJMqoKn4e6kaBApOU26u4 Zs1XpLoRpP I3v0yjUopIfBVCJRvdI4XwKN 0kdPsMB8qh7Wars3kWKy - quochung.cyou PTIT
BAnymKZ8nExl3pbZu5nN5V3odcCt frK0wqzoUd0 - quochung.cyou PTIT
  • JLabel – Vùng tiêu đề, chứa văn bản
- quochung.cyou PTIT
RA9XjSro1CdjqFw50fwWm3lDiS6BGp7e7UFOhF1kdhZRdTUFYbnZmpSC 4PnAjbKMr ki82BQzPkCoPQ1m M0QCuy96HEZbCpMZZZj72Au5x pqKwpaSEzJn6YYS35JLPgp8zON3HYlU - quochung.cyou PTIT
  • JComboBox – Menu lựa chọn
atRjeVfpNmYxmpwoEDcw4EJQP336OZtTeBWOqH8Mbf2Ic9IcR2IPC4wsd9H4AHEJgxsQjdDuKcCtqJ7wifjd7l7RxxZ2QVda1NE - quochung.cyou PTIT
kS lTUfDTWwP8WsDZtOoqdHzwv zrdLiu4SFBT TWoaIjCEoRrdXcegQbysqru1lAWNd7hnTxXYrAkPSmXMzCyXE5BDav XiJw1z3vVUGK4Lm zKSmd HHBGRmg5Cg0u 0AY GUqyeI3 - quochung.cyou PTIT