đồ án thiết kế trong các hệ thống nhỏ và trong các hệ thống lớn

104 0 0
Tài liệu đã được kiểm tra trùng lặp
đồ án thiết kế trong các hệ thống nhỏ và trong các hệ thống lớn

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

Thiết kế trong các hệ thống nhỏ và trong các hệ thống lớn Với các hệ thống nhỏ và tương đối đơn giản, thiết kế và kiến trúc của hệ thống không phải là một vấn đề quá phức tạp, do bản thâ

Trang 1

ĐẠI HỌC QUỐC GIA TP HỒ CHÍ MINH TRƯỜNG ĐẠI HỌC CÔNG NGHỆ THÔNG TIN

KHOA CÔNG NGHỆ PHẦN MỀM

GIẢNG VIÊN ThS Nguyễn Công Hoan

TP HỒ CHÍ MINH, 2023

Trang 2

với Spring Boot 2.0 Đặng Quốc Hùng – 20520194 Phùng Trần Đăng Khôi - 20520150

Trang 3

MỤC LỤC

1.1 Thiết kế trong các hệ thống nhỏ và trong các hệ thống lớn 1

Trang 4

4.2.4 Sidecar cache 16

Trang 6

8.5.1 Prometheus 60

9.3.2 Giao diện trừu tượng bộ nhớ cache của Spring 67 9.3.3 Một số khái niệm quan trọng và các chú thích về cache 68

9.4.1 Các tình huống ứng dụng của middleware tin nhắn 71

9.4.2 Tổng quan về phần mềm trung gian dịch vụ tin nhắn 73

Trang 7

9.6.2 Các kịch bản ứng dụng Docker 79

9.12.2 Vấn đề giao dịch phân tán trong microservices 86

Trang 8

9.13.1 Giới thiệu 89 9.13.2 Phụ bản Bean Validation nhúng các chú thích ràng buộc vào 90

9.17 Spring Boot với Hystrix Turbine and Dashboard 92

Trang 9

Chương 1: Tổng quan về thiết kế hệ thống

1.1 Thiết kế trong các hệ thống nhỏ và trong các hệ thống lớn

Với các hệ thống nhỏ và tương đối đơn giản, thiết kế và kiến trúc của hệ thống không phải là một vấn đề quá phức tạp, do bản thân các hệ thống này tương đối dễ tổ chức, dễ vận hành, dễ bảo trì và nhờ đó nên không có nhiều thành phần phức tạp Điển hình về các hệ thống ở quy mô này là các hệ thống quản lý kho, hệ thống quản lý hồ sơ, quản lý sản phẩm của riêng một công ty, tổ chức; hệ thống quản lý thư viện địa phương, Với các hệ thống này, có rất nhiều mô hình phát triển phần mềm đủ đáp ứng yêu cầu: kiến trúc Monolith, kiến trúc N-tier, mô hình 3 lớp, Clean architecture, Hexagonal architecture, Các mô hình kiến trúc này đều dựa trên một cơ sở để thực hiện là kiến trúc Client-server

Thiết kế điển hình của một hệ thống nhỏ

Trong các mô hình kiến trúc đã kể trên cũng như trong kiến trúc Client-server, người dùng (client) kết nối đến một chương trình được deploy trên một server cụ thể, server này có kết nối đến một database cụ thể để thực hiện lưu trữ Với các hệ thống có quy mô nhỏ và độ phức tạp tương đối đơn giản, phương pháp này hoàn toàn có thể đáp ứng đầy đủ các yêu cầu nghiệp vụ

Tuy nhiên, với các hệ thống có quy mô lớn hơn, mô hình nghiệp vụ phức tạp hơn, nhiều người sử dụng hơn, các mô hình kiến trúc kể trên không đủ khả năng để cung cấp dịch vụ kịp thời, chính xác và ổn định Điều này do giới hạn phần cứng của một server không thể nào đáp ứng được một nghìn, mười nghìn, thậm chí là cả triệu yêu cầu tại một thời điểm Do đó các hệ thống lớn cần phải áp dụng những kiến trúc phức tạp, đặc biệt hơn để đáp ứng nhu cầu chịu tải lớn, đảm bảo độ ổn định, độ chính xác

Trang 10

và có khả năng chịu lỗi Một hệ thống có các nhu cầu trên thông thường sẽ sử dụng nhiều server, mỗi server song song chạy chương trình và nhiều database để đáp ứng nhu cầu lưu trữ

Thiết kế điển hình của một hệ thống lớn

1.2 Các nguyên lý thiết kế hệ thống

1.2.1 Scalability

Scalability là khả năng tăng trưởng (scale) của một hệ thống để đáp ứng nhu cầu xử

lý, tức khả năng xử lý tối ưu hơn, xử lý được nhiều yêu cầu hơn của một hệ thống khi có thêm tài nguyên (bộ nhớ, đĩa cứng, tài nguyên CPU, )

Có 2 cách để scale một hệ thống là vertical scaling và horizontal scaling Vertical

scaling là tăng trưởng hệ thống bằng cách nâng cấp các thành phần vật lý của server

như nâng cấp CPU, nâng cấp RAM, nâng ổ cứng, Ngược lại, horizontal scaling là

cách thêm nhiều server hơn vào hệ thống, dùng các server phối hợp nhau để xử lý

nguồn tải lớn bằng cách chia đều tải (cân bằng tải) cho mỗi server

Ta không thể áp dụng vertical scaling để scale một hệ thống vĩnh viễn được do hạn chế về phần cứng - chỉ có thể lắp đặt phần cứng tối tân nhất cho một server, khi yêu cầu xử lý vượt quá khả năng của các phần cứng này, hệ thống sẽ bắt đầu ngưng trệ và suy thoái Đây là giới hạn của vertical scaling

Ngược lại, áp dụng horizontal scaling sẽ cho phép scale hệ thống không giới hạn chỉ bằng cách cài đặt thêm các server Song, horizontal scaling cũng phức tạp hóa mô hình kiến trúc của hệ thống do phải điều phối hoạt động của nhiều server riêng biệt

và vận hành như một bộ máy duy nhất Kiến trúc này được biết đến với tên gọi Hệ

thống phân tán (Distributed system) và là cơ sở cho ra đời một họ các mô hình kiến

trúc sau này như Distributed monolith, Microservices, cùng với đó là rất nhiều vấn

đề cực kỳ phức tạp và khó giải quyết khác

Khi nhắc đến scalability, hầu hết ta luôn muốn nói đến khả năng horizontal scaling của hệ thống

Trang 11

1.2.2 Availability

Availability là độ sẵn sàng xử lý yêu cầu của hệ thống Các hệ thống lớn có yêu cầu về availability cao, tức hệ thống phải thường xuyên sẵn sàng xử lý yêu cầu Availability có thể được miêu tả bằng các thuật ngữ như:

● “Two-nines” (hai số chín), tức hệ thống phải sẵn sàng trong 99% thời gian tồn

tại, tương đương với hệ thống chỉ được phép sập tối đa 3.7 ngày/năm

● “Three-nines”, tức sẵn sàng trong 99.9% thời gian tồn tại, tương đương thời

gian không hoạt động 8.8 tiếng/năm

● “Four nines” - 99.99% available, sập 53 phút/năm ● “Five-nines” - 99.999% available, sập 5.3 phút/năm

Để tăng cường availability, ta sử dụng các phương pháp như failover và replication

Failover có 2 cách: chủ động-thụ động passive) và chủ động-chủ động active)

(active-Với failover chủ động-thụ động, heartbeat (nhịp tim) được gửi giữa server chủ động

và server thụ động ở chế độ chờ Nếu nhịp tim bị gián đoạn, server thụ động sẽ tiếp nhận địa chỉ IP của server đang hoạt động và tiếp tục dịch vụ Khoảng thời gian ngừng hoạt động được xác định bằng việc server thụ động đã chạy ở chế độ chờ 'nóng' hay cần khởi động từ chế độ chờ 'nguội' Chỉ có server chủ động xử lý yêu cầu Failover chủ động-thụ động cũng có thể được gọi là failover chủ-tớ (master-slave)

Trong trạng thái chủ động-chủ động, cả hai server đều xử lý yêu cầu, tải được cân bằng giữa chúng Nếu các server ở chế độ công khai, thì DNS sẽ cần biết về các IP

công khai của cả hai server để có thể hỗ trợ client-side load balancing Nếu các server

chỉ giao tiếp nội bộ, application logic sẽ cần biết về cả hai server Failover chủ chủ động cũng có thể được gọi là failover chủ-chủ (master-master)

động-Replication cũng có 2 cách: master-slave (master-replica) và master-master master)

(multi-Trong master-slave replication, master xử lý cả yêu cầu đọc và ghi, dữ liệu thay đổi từ các yêu cầu ghi được sao chép (replicate) sang các slave; slave chỉ xử lý đọc Slave cũng có thể sao chép dữ liệu ghi sang các slave khác Nếu master gặp sự cố, hệ thống có thể tiếp tục hoạt động ở chế độ read-only cho đến khi một slave được thăng thành master hoặc một master mới được thêm vào cluster

Trang 12

Ngược lại, trong master-master replication, các master cùng xử lý cả yêu cầu đọc và ghi, phối hợp với nhau để đảm bảo nhất quán trong các yêu cầu ghi Khi một server master có sự cố, các master còn lại vẫn có thể tiếp tục xử lý đọc và ghi

1.2.3 Consistency

Consistency (tính nhất quán) là thuộc tính của hệ thống phân tán đảm bảo rằng mọi server trong cluster đều có cùng trạng thái, cùng dữ liệu tại một thời điểm nhất định, bất kể client nào đã cập nhật dữ liệu Tính nhất quán cao có nghĩa là hệ thống phân tán hội tụ trên một giá trị duy nhất và client luôn đọc được dữ liệu mới nhất

Tính nhất quán của một hệ thống bị chi phối bởi định lý CAP - nêu rằng một hệ thống chỉ có thể đảm bảo được 2 trong 3 tính chất: Consistency (tính nhất quán), Availability (tính sẵn sàng) và Partition Tolerance (chịu được phân vùng) Nói cách khác, khi xảy ra network partition thì một hệ thống chỉ có thể đảm bảo được 1 trong 2 - Consistency hoặc Availability Khi không có network partition thì một hệ thống có thể đảm bảo cả hai

Có nhiều mô hình về yêu cầu tính nhất quán của một hệ thống:

● Strong consistency: là mô hình nhất quán mạnh nhất, cam kết rằng mọi thao

tác được thực hiện lên dữ liệu đều được phản ảnh ngay lập tức bởi tất cả server trong cluster và do đó client luôn quan sát được dữ liệu mới nhất Mô hình này hy sinh availability để đảm bảo sự nhất quán giữa các server

● Weak consistency (hoặc Eventual consistency): các thao tác sau khi thực hiện

có thể chưa được phản ánh bởi các server trong cluster, yêu cầu đọc của client có thể chưa nhìn thấy dữ liệu mới nhất, trả về dữ liệu cũ Mô hình này không đảm bảo tính nhất quán cao nhưng đổi lại availability cao

1.2.4 Fault tolerance

1.2.4.1 Các khái niệm về fault tolerance

● Failure: cả hệ thống bị gián đoạn, không hoạt động ● Fault: một phần của hệ thống không hoạt động

○ Node fault: sự cố tại một server node

■ Crash-stop: server sập và dừng hoàn toàn, không hồi phục ■ Crash-recovery: server sập và sau đó hồi phục trở lại ■ Byzantine fault: server gặp sự cố và hoạt động không theo

chương trình, xử lý một cách ngẫu nhiên, không đáng tin cậy

Trang 13

Byzantine fault có thể do trục trặc trong các thiết bị điện tử chứ

không nhất thiết phải do sự can thiệp của tác nhân có ý đồ xấu

○ Network fault: sự cố về đường truyền, kết nối mạng, chẳng hạn như message bị mất hoặc delay, tắc nghẽn,

● Fault tolerance: khả năng chống fault - hệ thống vẫn tiếp tục hoạt động khi

có fault (số lượng fault dưới một ngưỡng tối đa)

● Single point of failure: khi fault ở một node dẫn tới failure trên toàn hệ thống

1.2.4.2 Byzantine fault

Lỗi Byzantine (Byzantine fault), hay còn gọi là Vấn đề các vị tướng Byzantine, là một

tình trạng của một hệ thống, đặc biệt là hệ thống phân tán, trong đó các component có thể bị lỗi và không có thông tin chính xác về việc liệu component đó có bị lỗi hay không Thuật ngữ này lấy tên từ một câu chuyện ngụ ngôn, "Vấn đề các vị tướng Byzantine", được phát triển để mô tả một tình huống trong đó, để tránh sự thất bại thảm hại của hệ thống, các tác nhân của hệ thống phải đồng ý về một chiến lược phối hợp, nhưng một số tác nhân này là không đáng tin cậy

Vấn đề các vị tướng Byzantine

Một server trong một cluster có thể bị lỗi vì nhiều lý do, mà khiến cho server hoạt động không đúng theo chương trình, lệch khỏi chương trình, khiến cho mọi phản hồi từ server này trở nên không đáng tin cậy

Trang 14

Không có cách nhất định để phát hiện lỗi Byzantine, do các server bị lỗi hoàn toàn có thể phản hồi lại các yêu cầu một cách bình thường nhưng thực chất không thực hiện yêu cầu theo đúng chương trình Node bị lỗi còn có thể phản hồi thông tin khác nhau với mỗi thiết bị quan sát khác nhau Do đó, các cơ chế phát hiện lỗi (fault detection) thông thường không thể nhận biết được liệu một node có bị lỗi Byzantine hay không

Không có cách để một tướng biết được trong số các tướng khác ai là người phản bội

Tên gọi Byzantine xuất phát từ một sự so sánh rằng tình trạng này giống với một trận đánh mà các tướng quân đang quyết định có đánh chiếm thành Byzantine không Các tướng quân cần thống nhất một kế hoạch đánh hay rút lui, nhưng trong số các vị tướng có một số kẻ phản bội, chúng có thể biểu quyết đánh nhưng sau đó lại rút lui Trong một cluster có 3 node, nếu một node bị lỗi Byzantine, cả cluster sẽ không thể cung cấp dịch vụ do không thể đạt thống nhất về trạng thái và cũng không tìm ra được node bị lỗi

Lỗi Byzantine chỉ có thể giải được trong một cluster có số node bị lỗi ít hơn ⅓ tổng số node Lỗi Byzantine cũng có ảnh hưởng đến các thuật toán đồng thuận (Consensus)

Trang 15

Chương 2: Load balancer 2.1 Load balancer

Load balancer là một thiết bị phân phối lưu lượng mạng hoặc ứng dụng trên nhiều máy chủ Load balancer được sử dụng để tăng khả năng xử lý và availability của ứng dụng Chúng cải thiện hiệu suất tổng thể của ứng dụng bằng cách giảm gánh nặng cho máy chủ liên quan đến việc quản lý và duy trì phiên ứng dụng và mạng, cũng như thực hiện các tác vụ cụ thể của ứng dụng

Load balancer có thể giải quyết một số vấn đề, bao gồm:

● Khả năng mở rộng: Khi lưu lượng truy cập tăng, load balancer có thể phân phối lưu lượng truy cập trên nhiều máy chủ, giúp dễ dàng mở rộng theo chiều ngang

● Dự phòng: Load balancer có thể giúp đảm bảo tính sẵn sàng của ứng dụng bằng cách chuyển hướng lưu lượng truy cập đến các máy chủ khác khi một máy chủ gặp sự cố

● Hiệu suất: Load balancer có thể giúp cải thiện hiệu suất của ứng dụng bằng cách phân phối lưu lượng truy cập đến các máy chủ ít bận rộn hơn

● Bảo mật: Load balancer có thể giúp bảo vệ ứng dụng khỏi các cuộc tấn công từ chối dịch vụ (DoS) và các cuộc tấn công khác bằng cách chặn lưu lượng truy cập độc hại

2.2 Phân loại

2.2.1 Hardware & Software

Cách phân loại thứ nhất của load balancer là phân loại theo cách thức deploy và nó có hai loại:

Trang 16

● Hardware load balancer: Là một thiết bị phần cứng vật lý được cài

đặt trong trung tâm dữ liệu Nó được thiết kế để xử lý lưu lượng truy cập lớn và có hiệu suất cao Hardware load balancer thường có giá thành cao và đòi hỏi phải có kiến thức chuyên môn để cài đặt và cấu hình

● Software load balancer: Là một ứng dụng phần mềm có thể được cài

đặt trên physical host hoặc virtual host Nó có thể hoạt động như một ứng dụng độc lập hoặc được tích hợp vào hệ thống hiện có Software load balancer linh hoạt hơn và có chi phí thấp hơn so với hardware load balancer

2.2.2 Tầng xử lý

Cách phân loại thứ hai là sử dụng tầng xử lý của load balancer:

● Layer 7 load balancer: Layer 7 load balancer hoạt động ở tầng ứng

dụng (application layer) của mô hình OSI và sử dụng thông tin từ giao thức ứng dụng (chủ yếu là HTTP) để đưa ra quyết định định tuyến Layer 7 load balancer hoạt động như một proxy, duy trì hai kết nối TCP: một với client và một với server

Trang 17

● Layer 3/4 load balancer: Layer 3/4 load balancer hoạt động ở tầng

mạng (network layer) và tầng vận chuyển (transport layer) của mô hình OSI và sử dụng thông tin về địa chỉ IP và cổng TCP/UDP để đưa ra quyết định định tuyến Layer 3/4 load balancer thường không kiểm tra nội dung gói tin và chỉ chuyển tiếp các gói tin TCP/UDP đến các máy chủ backend

● Global server load balancer: Global server load balancer (GSLB) là

một phương pháp phân phối lưu lượng truy cập trên các máy chủ được đặt tại nhiều địa điểm trên toàn cầu GSLB sử dụng DNS và kiến thức về tài nguyên dịch vụ để điều hướng lưu lượng truy cập dựa trên logic kinh doanh được xác định GSLB giúp tăng độ tin cậy và giảm độ trễ cho người dùng

Trang 18

trọng số cho các máy chủ để ưu tiên một số máy chủ hơn

● Least Connections: Chuyển hướng lưu lượng truy cập đến các máy chủ có ít

kết nối nhất

● Weighted Least Connections: Tương tự như Least Connections, nhưng cho

phép gán trọng số cho các máy chủ để ưu tiên một số máy chủ hơn

● IP Hash: Sử dụng địa chỉ IP nguồn và đích của lưu lượng truy cập để tính toán

một giá trị băm và gán kết nối đến một máy chủ cụ thể dựa trên giá trị băm này

2.4 Patterns

Server-side load balancing và client-side load balancing đều là các phương pháp phân phối lưu lượng truy cập trên nhiều máy chủ, nhưng chúng khác nhau về cách thức hoạt động:

● Server-side load balancing: Server-side load balancing được thực hiện bởi

một thiết bị hoặc ứng dụng được đặt trước các máy chủ backend Load balancer sẽ nhận các yêu cầu từ client và sử dụng một thuật toán để chọn một máy chủ backend để xử lý yêu cầu Sau đó, load balancer sẽ chuyển tiếp yêu cầu đến máy chủ backend được chọn và trả về kết quả cho client

Trang 19

● Client-side load balancing: Client-side load balancing được thực hiện bởi

client Client sẽ nhận thông tin về các máy chủ backend từ một dịch vụ danh mục (registry service) và sử dụng một thuật toán để chọn một máy chủ backend để gửi yêu cầu Sau đó, client sẽ gửi yêu cầu trực tiếp đến máy chủ backend được chọn và nhận kết quả trả về

Cả hai loại load balancing đều có ưu và nhược điểm riêng Server-side load balancing đơn giản hơn và dễ triển khai hơn, nhưng có thể tạo ra bottleneck và tăng độ trễ Client-side load balancing linh hoạt hơn và giảm độ trễ, nhưng phức tạp hơn và đòi hỏi client phải có khả năng thực hiện load balancing

2.5 Tăng availability của hệ thống

Single point of failure (SPOF) là một điểm trong hệ thống mà nếu nó gặp sự cố, toàn bộ hệ thống sẽ ngừng hoạt động Việc có một SPOF trong hệ thống có thể dẫn đến nhiều vấn đề, bao gồm:

Trang 20

● Mất dữ liệu: Nếu SPOF là một thiết bị lưu trữ dữ liệu, sự cố có thể dẫn đến

mất dữ liệu quan trọng

● Thời gian chết: Khi SPOF gặp sự cố, toàn bộ hệ thống sẽ ngừng hoạt động,

dẫn đến thời gian chết và ảnh hưởng đến trải nghiệm người dùng

● Mất doanh thu: Thời gian chết có thể dẫn đến mất doanh thu, đặc biệt đối với

các doanh nghiệp phụ thuộc vào hệ thống để bán hàng hoặc cung cấp dịch vụ

● Mất uy tín: Sự cố liên tục có thể làm giảm uy tín của doanh nghiệp và ảnh

hưởng đến lòng tin của khách hàng

Để giảm thiểu rủi ro từ SPOF, ta có thể áp dụng một số phương pháp để tạo ra bản sao của các thành phần trong hệ thống rồi load balance giữa các instance đó

Ta có thể khắc phục SPOF cho database sử dụng Sharding và replication Sharding và replication là hai phương pháp phân tán dữ liệu và tài nguyên trong các hệ thống lớn, không chỉ áp dụng cho cơ sở dữ liệu mà còn cho các dịch vụ khác

● Sharding: Sharding là một phương pháp phân chia dữ liệu hoặc tài nguyên

lớn thành nhiều phần nhỏ hơn và lưu trữ chúng trên nhiều máy chủ khác nhau Mỗi máy chủ sẽ lưu trữ một phần của dữ liệu hoặc tài nguyên và chỉ xử lý các yêu cầu liên quan đến phần đó Sharding giúp tăng khả năng mở rộng và cải thiện hiệu suất của hệ thống bằng cách giảm gánh nặng cho từng máy chủ và cho phép xử lý song song các request

● Replication: Replication là một phương pháp sao chép dữ liệu hoặc tài nguyên

từ một máy chủ sang một hoặc nhiều máy chủ khác Mục đích của replication là tăng độ tin cậy và khả dụng của hệ thống bằng cách cung cấp các bản sao dự phòng của dữ liệu hoặc tài nguyên Khi một máy chủ gặp sự cố, các máy chủ khác có thể tiếp tục hoạt động và đảm bảo availability của dịch vụ

Trang 21

Cả hai phương pháp đều có ưu và nhược điểm riêng và có thể được sử dụng kết hợp với nhau để tăng cường khả năng mở rộng, hiệu suất, độ tin cậy và khả dụng của hệ thống

Trang 22

Chương 3: Caching 3.1 Tổng quan về caching

Caching là phương pháp lưu kết quả của các thao tác, các phép tính trước vào một bộ nhớ dễ truy cập và có độ trễ nhỏ, để phục vụ cho việc tái sử dụng trong tương lai Các công dụng chính của caching:

● Giảm thời gian phản hồi, tăng performance, scalability, availability ● Giảm tải lên các server

● Giảm nguy cơ quá tải các server từ tấn công DoS Caching có thể được thực hiện ở nhiều vị trí trong hệ thống

Cache ở nhiều vị trí trong hệ thống

3.2 Các pattern trong caching

4.2.1 Local cache

Ưu điểm:

● Đơn giản, độ trễ thấp, không phải serialize/deserialize

Trang 23

● Có khả năng chịu lỗi và phục hồi

● Có thể sử dụng các thuật toán băm (hash) để phân mảnh dữ liệu, lưu trên nhiều node

● Sử dụng các data store như Redis, Memcached, SQL Server làm backing data store

Nhược điểm:

● Tăng độ trễ so với local cache

Trang 24

4.2.3 Reverse proxy cache

Trang 25

● Độ trễ thấp hơn cache phân tán do cache và chương trình nằm trong cùng một pod

● Cấu hình đơn giản

● Tách biệt chương trình và cache, giảm dependency Nhược điểm:

● Chỉ thực sự tối ưu khi áp dụng trong môi trường Kubernetes ● Độ trễ cao hơn local cache

4.2.5 Reverse proxy sidecar cache

Ưu điểm:

● Độ trễ thấp do cache và chương trình nằm trong cùng một pod ● Thích hợp với kiến trúc Microservice, Kubernetes, Service mesh ● Có khả năng cache API response

Nhược điểm:

● Khó invalidate cache

Trang 26

3.3 Làm trống cache (Cache eviction)

Cache là một bộ nhớ có độ trễ truy cập nhỏ hơn nhiều lần so với các database, nhưng đổi lại thì dung lượng của cache cũng nhỏ hơn nhiều so với các ổ đĩa cứng sử dụng để lưu trữ dữ liệu trong các database Nguyên nhân là do phần cứng hỗ trợ của cache thường là bộ nhớ chính (RAM) hoặc các register có dung lượng nhỏ hơn nữa Do đó nên ta cần sử dụng dung lượng trong cache để ưu tiên cho các dữ liệu quan trọng, được truy cập nhiều hoặc có chi phí tính toán cao Có nhiều thuật toán được sử dụng

để xác định mức ưu tiên của dữ liệu, gọi chung là các thuật toán làm trống cache

(cache eviction policy):

● Least recently used (LRU): loại bỏ các dữ liệu được truy cập ít nhất trong

khoảng thời gian gần nhất để ưu tiên cho các dữ liệu được sử dụng tức thì

● Least frequently used (LFU): loại bỏ các dữ liệu được sử dụng ít thường xuyên

nhất để ưu tiên cho các dữ liệu được sử dụng nhiều hơn; thuật toán này ghi nhớ tần suất truy cập của mỗi entry dữ liệu

● Most recently used (MRU): loại bỏ các dữ liệu được sử dụng gần đây nhất, ưu

tiên các dữ liệu chưa được sử dụng; thuật toán này thường được ứng dụng trong các hệ thống gợi ý dữ liệu mới, chẳng hạn như Tinder

● Random replacement: loại bỏ dữ liệu một cách ngẫu nhiên

3.4 Các pattern truy cập trong caching

4.4.1 Cache-aside

Chương trình luôn đọc dữ liệu từ cache trước, nếu cache không có dữ liệu đó thì chương trình sẽ đọc từ database rồi ghi vào cache để hỗ trợ những lần đọc sau; ngược lại, cache sẽ trả kết quả ngay lập tức cho chương trình

Trang 27

Ưu điểm:

● Đơn giản, dễ thực hiện

● Data model trong cache có thể khác so với database Nhược điểm:

● Cần 3 network trip khi cache miss

● Khi update database, dữ liệu trong cache sẽ bị stale

4.4.2 Read-through

Cache đóng vai trò như một lớp trung gian giữa chương trình và database Chương trình luôn đọc từ cache, nếu không có dữ liệu đó, cache sẽ tự đọc từ database và ghi vào bộ nhớ của mình, sau đó trả kết quả cho chương trình

Trang 28

● Đảm bảo cache và database luôn nhất quán, dữ liệu trong cache không bị lạc hậu

Trang 29

Chương 4: Microservices 4.1 Miêu tả microservice

Một hệ thống monolithic là một hệ thống phần mềm trong đó tất cả các chức năng và tính năng đều được tích hợp chặt chẽ với nhau Mặc dù kiến trúc monolithic có thể đơn giản hóa việc phát triển và triển khai ban đầu, nhưng nó cũng có thể gây ra một số vấn đề khi hệ thống phát triển và mở rộng Một số vấn đề của một hệ thống monolithic bao gồm:

● Khó mở rộng: Khi lưu lượng truy cập tăng, việc mở rộng một hệ thống

monolithic có thể trở nên khó khăn do sự phụ thuộc chặt chẽ giữa các thành phần

● Khó bảo trì: Việc bảo trì và nâng cấp một hệ thống monolithic có thể trở nên

phức tạp và tốn kém do sự phụ thuộc chặt chẽ giữa các thành phần

● Khó tái sử dụng: Các thành phần trong một hệ thống monolithic thường khó

tái sử dụng trong các dự án khác do sự phụ thuộc chặt chẽ giữa chúng

● Khó kiểm thử: Việc kiểm thử một hệ thống monolithic có thể trở nên khó

khăn do sự phụ thuộc chặt chẽ giữa các thành phần

● Code base lớn: Các lập trình viên mới vào dự án sẽ khó nắm bắt được project

vì lượng thông tin rất lớn

● Không có tính cách ly: Các thành phần khác nhau có thể ảnh hướng đến nhau

do trong cùng một codebase nên việc xảy ra lỗi là điều rất khó tránh khỏi Để giải quyết các vấn đề này, nhiều tổ chức đã chuyển sang sử dụng kiến trúc microservice Microservice là một kiến trúc phần mềm trong đó ứng dụng được xây dựng dưới dạng một tập hợp các service nhỏ và độc lập, có thể triển khai và quản lý một cách độc lập Kiến trúc microservice mang lại nhiều lợi ích, bao gồm:

● Khả năng mở rộng: Các dịch vụ có thể được mở rộng độc lập, giúp tăng

cường khả năng xử lý của hệ thống

● Dễ bảo trì: Việc bảo trì và nâng cấp các dịch vụ trở nên dễ dàng hơn do chúng

được quản lý một cách độc lập

● Tái sử dụng: Các dịch vụ có thể được tái sử dụng trong các dự án khác, giúp

tiết kiệm thời gian và chi phí phát triển

● Dễ kiểm thử: Việc kiểm thử các service trở nên dễ dàng hơn do chúng được

quản lý một cách độc lập

● Life cycle độc lập: Các service sẽ có life cycle riêng và không phụ thuộc lẫn

nhau giúp các team có thể thoải mái hơn trong việc đặt version và release

Trang 30

● Code base nhỏ: Mỗi team sẽ chỉ cần quan tâm và quản lý codebase riêng của service mà họ đang phát triển

● Tính cách ly cao: Khi có lỗi xảy ra ta có thể dễ dàng xác định được là nó nằm

gọn trong dịch vụ nào mà không cần phải lo về việc lỗi xảy ra ở một dịch vụ

khác nhưng lại do update một dịch vụ khác

● Deploy nhanh: Do life cycle độc lập và các service có thể deploy riêng nên

việc dừng các tiến trình đang chạy để khởi tạo các tiến trình mới với các update

mới nhanh hơn nhiều so với monolithic system

4.2 Phương thức giao tiếp giữa các service

Khi chuyển từ một hệ thống monolithic sang một hệ thống microservice, một trong những thách thức lớn nhất là việc thiết lập giao tiếp giữa các dịch vụ Trong một hệ thống monolithic, các thành phần khác nhau của ứng dụng có thể giao tiếp với nhau một cách trực tiếp thông qua các lời gọi hàm hoặc các cơ chế khác Tuy nhiên, trong một hệ thống microservice, các dịch vụ được triển khai và quản lý một cách độc lập, do đó chúng không thể giao tiếp trực tiếp với nhau

Để giải quyết vấn đề này, ta có thể sử dụng nhiều kỹ thuật khác nhau để thiết lập giao tiếp giữa các dịch vụ Một số kỹ thuật phổ biến bao gồm:

● RESTful API: Các dịch vụ có thể cung cấp các API theo chuẩn RESTful để

cho phép các dịch vụ khác gọi và trao đổi dữ liệu

● Message queue: Các dịch vụ có thể sử dụng message queue để gửi và nhận

các thông điệp không đồng bộ

Trang 31

● Service mesh: Service mesh là một lớp trung gian giữa các dịch vụ, cung cấp

các tính năng như cân bằng tải, bảo mật và giám sát để giúp quản lý giao tiếp giữa các dịch vụ

4.3 Khuyết điểm của microservice

Microservices là một kiến trúc phần mềm phổ biến, nhưng cũng có một số khuyết điểm cần lưu ý Dưới đây là một số khuyết điểm của microservices:

● Phức tạp: Việc triển khai và quản lý một hệ thống microservices có thể phức

tạp hơn so với một ứng dụng monolithic Các nhà phát triển cần phải xử lý các vấn đề như phân tán dữ liệu, giao tiếp giữa các dịch vụ và quản lý phiên bản

● Chi phí: Việc triển khai và vận hành một hệ thống microservices có thể đắt

hơn so với một ứng dụng monolithic do yêu cầu nhiều tài nguyên hơn, chẳng hạn như máy chủ và bộ nhớ

● Thời gian triển khai ban đầu: Việc triển khai một hệ thống microservices có

thể mất nhiều thời gian hơn so với một ứng dụng monolithic do yêu cầu nhiều bước hơn, chẳng hạn như đóng gói và triển khai từng dịch vụ riêng biệt

Trang 32

● Rủi ro bảo mật: Việc sử dụng nhiều dịch vụ riêng biệt có thể tạo ra nhiều

điểm yếu hơn cho các cuộc tấn công mạng Các nhà phát triển cần phải đảm bảo rằng tất cả các dịch vụ đều được bảo mật đúng cách

● Yêu cầu nhân lực nhiều hơn: Khi triển khai một hệ thống microservice thì

ta cần phải có một đội ngũ các DevOps làm việc với nhau để có thể đưa hệ

thống từ development lên production

Tuy nhiên, nếu được thiết kế và triển khai đúng cách, microservices có thể mang lại nhiều lợi ích cho doanh nghiệp, chẳng hạn như khả năng mở rộng linh hoạt, availability cao và khả năng phát triển nhanh chóng Do đó, việc lựa chọn giữa microservices và kiến trúc monolithic sẽ phụ thuộc vào yêu cầu và mục tiêu của doanh nghiệp

Trang 33

Chương 5 : Service discovery & API Gateway

Service Discovery là một phần quan trọng trong việc quản lý các service trong một distributed system Khi có nhiều service chạy cùng lúc, việc tìm địa chỉ IP và port của một service cụ thể có thể trở nên phức tạp Đặc biệt khi chạy trên cloud, địa chỉ IP của các service có thể thay đổi thường xuyên do việc tạo và xóa máy ảo hay container là chuyện thường trong môi trường cloud

Service Discovery giúp giải quyết vấn đề này bằng cách cho phép các service đăng ký địa chỉ IP, port và tên của chúng với một registry Các service khác có thể registry này để lấy thông tin về các service khác trong hệ thống Nhờ vậy, các service có thể nói chuyện trực tiếp với nhau một cách dễ dàng hơn

Có hai phương thức áp dụng service discovery: ● Client side

● Server side

Có hai cách để register service: ● Self registration

● Third-party registration

5.1 Phân loại service discovery

Client side: Client-side Service Discovery, client hoặc API gateway thực hiện yêu

cầu chịu trách nhiệm xác định vị trí của service instance và định tuyến yêu cầu đến nó Client bắt đầu bằng cách truy vấn service registry để xác định vị trí của các instance của service có sẵn và sau đó xác định instance nào sẽ sử dụng

Trang 34

Server side: Trong khi đó, với Server-side Service Discovery, client không cần phải

biết về service registry Các yêu cầu được thực hiện thông qua một router, sau đó router tìm kiếm service registry Ví dụ về Server-side Discovery là AWS Elastic Load Balancer (ELB)

5.2 Phân loại hình thức register

Trong Service Discovery, có hai cách chính để đăng ký một service với registry: đăng ký tự động và đăng ký thủ công

● Self registration: Trong trường hợp này, service sẽ tự động đăng ký với

registry khi nó khởi động Điều này thường được thực hiện bằng cách sử dụng một thư viện client tích hợp với service, cho phép service giao tiếp với registry và cung cấp thông tin cần thiết để đăng ký

● Third party registration: Trong trường hợp này, service sẽ được tự động

đăng ký mà không cần phải sử dụng thư viện tích hợp Khi này việc phát hiện ra service và register và của một phần khác của hệ thống và service sẽ không cần phải biết bất cứ thứ gì về registry Điều này giúp decoupled registry với service nhưng lại kho implement hơn hẳn so với self registration Mặt khác, ta không bị giới hạn bởi ngôn ngữ mà thư viện để register hỗ trợ

Trang 35

5.3 Sử dụng service

1.1 Direct

Cách thức đầu tiên ta có thể sử dụng service là sử dụng trực tiếp thông qua service discovery Nhưng điều này là không nên vì để làm như thế này thì ta cần phải expose các địa chỉ IP của service và dẫn đến một lỗ hổng trong bảo mật Mặt khác các request sẽ trở nên phức tạp hơn vì ta sẽ cần phải tổng hợp lại các thông tin mà ta thu thập từ nhiều service khác nhau, ta phải biết service nào cung cấp thông tin nào

1.2 Composite UI

Một cách khác là ta dùng composite UI, khi này ta sẽ thiết kế giao diện dựa trên các service trong kiến trúc microservice ở backend Thay vì ta có một monolithic UI thì bây giờ ta sẽ ánh xạ kiến trúc của backend và thiết kế quanh các service Việc thiết kế giao diện này sẽ yêu cầu nhiều kỹ năng hơn từ người lập trình nhưng sẽ giúp phân tách nhiệm vụ truy vấn dữ liệu ra Tuy nhiên cách này vẫn cần phải expose địa chỉ IP, ta có thể giải quyết bằng cách áp dụng một API gateway

Trang 36

1.3 API Gateway

API Gateway là một thành phần trung gian giữa các client và các service trong một distributed system Nó đóng vai trò như một cửa vào cho các yêu cầu từ client, xử lý các yêu cầu đó và redirect chúng đến service thích hợp

API Gateway giải quyết một số vấn đề quan trọng trong việc xây dựng và vận hành distributed system Một số vấn đề mà API Gateway giải quyết bao gồm:

● Định tuyến yêu cầu: API Gateway có thể định tuyến các yêu cầu từ

client đến service thích hợp dựa trên các tiêu chí như URL, phương thức HTTP hoặc thông tin trong header

● Authentication & Authorization: API Gateway có thể authorize và

authenticate các yêu cầu từ client trước khi chuyển chúng đến service Điều này giúp lập trình viên không cần phải authorize ở code của service mà có thể để API Gateway authorize rồi để service authenticate quyền hạn

● Giới hạn tốc độ: API Gateway có thể giới hạn số lượng yêu cầu từ một

client trong một khoảng thời gian nhất định để ngăn chặn các cuộc tấn công từ chối dịch vụ (DoS) hoặc giảm thiểu các request bất thường

● Chuyển đổi dữ liệu: API Gateway có thể chuyển đổi dữ liệu từ một

định dạng sang một định dạng khác để phù hợp với yêu cầu của client hoặc service

● TLS termination: Giúp tăng tốc độ giao tiếp giữa các service trong cùng datacenter rồi re-encrypt khi reply được gửi đến gateway

Trang 37

● Decouple giao diện: Giao diện không dính với các service phía sau nên

ta có thể thay đổi thiết kế của hệ thống, chỉ cần đảm bảo api gateway

vẫn cung cấp thông tin đúng format là UI vẫn chạy

Tuy nhiên, Khi redirect thẳng các request từ api gateway đến microservice như hình trên thì có thể dẫn đến việc có quá nhiều service call

Thay vì redirect trực tiếp đến các microservice thì ta có thể thêm vào một trung gian gọi là aggregator

Đối với các request phức tạp cần thông tin từ nhiều services khác nhau thì ta sẽ redirect đến aggregator, còn các request đơn giản thì ta sẽ redirect nó đến service

Trang 38

Các thành phần chi tiết của một API Gateway được thể hiện ở sơ đồ bên dưới bao gồm các thành phần để xử lý lỗi và thu thập metrics

5.4 Envoy

5.4.1 Giới thiệu

Envoy Proxy là một L3/L4 và L7 proxy có thể được dùng như edge proxy, được thiết kế cho các ứng dụng cloud Được phát triển tại Lyft, Envoy là một proxy được viết bằng C++ với hiệu suất cao được thiết kế cho các hệ thống distributed và ứng dụng khác

Envoy được xây dựng trên những kiến thức có được từ các nền tảng đi trước như NGINX, HAProxy, envoy được deploy với các application và ẩn đi sự phức tạp của networking Khi ta áp dụng Envoy, ta có thể dễ dàng thu thập metrics để monitor hệ thống

Trang 39

Có thể ứng dụng làm API gateway nhờ các tính năng chủ yếu: ● Load balancing

● Service discovery ● Health checking ● Resiliency policies ● JWT Authentication ● Authorization

● TLS termination

5.4.2 Kiến trúc

Envoy bao gồm nhiều thành phần khác nhau để cung cấp các tính năng phong phú và linh hoạt Một số thành phần chính của Envoy Proxy bao gồm:

● Listeners: Là các thành phần lắng nghe các kết nối đến từ client và

chuyển chúng đến các filter chain thích hợp

● Filters: Là các thành phần xử lý dữ liệu truyền qua Envoy Có nhiều

loại filter khác nhau, bao gồm network filter, HTTP filter và listener filter

● Filter chains: Là một chuỗi các filter được sắp xếp theo thứ tự để xử

lý dữ liệu truyền qua Envoy Mỗi listener có thể có nhiều filter chain khác nhau và một filter chain mặc định tùy chọn

● Routes: Là các quy tắc redirect request từ client đến các cluster thích

hợp

● Clusters: Là một nhóm các máy chủ cung cấp cùng một dịch vụ Envoy

sử dụng thông tin về cluster để redirect request từ client đến máy chủ thích hợp

● Hosts: Là các máy chủ trong một cluster Envoy sử dụng thông tin về

host để redirect request từ client đến máy chủ thích hợp

Trang 40

Envoy sử dụng protocol buffer để định nghĩa các thuộc tính có thể được configure (listener, route, endpoint, filter, ), qua đó cho phép tùy ý extend các configuration đó

Các configuration có thể được fetch từ một management server bằng gRPC/HTTP polling và áp dụng ở runtime

Ví dụ về file yaml để configure Envoy:

Ngày đăng: 15/05/2024, 09:26

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan