Mẫu kiến ​​trúc tiện lợi

Này Habr!

Trước các sự kiện hiện tại do virus Corona, một số dịch vụ Internet đã bắt đầu nhận được lượng tải tăng lên. Ví dụ, Một trong những chuỗi bán lẻ ở Anh vừa dừng trang web đặt hàng trực tuyến của mình., vì không đủ năng lực. Và không phải lúc nào cũng có thể tăng tốc máy chủ chỉ bằng cách bổ sung thêm thiết bị mạnh hơn mà các yêu cầu của khách hàng phải được xử lý (nếu không chúng sẽ đến tay đối thủ cạnh tranh).

Trong bài viết này, tôi sẽ nói ngắn gọn về các phương pháp phổ biến sẽ cho phép bạn tạo một dịch vụ nhanh chóng và có khả năng chịu lỗi. Tuy nhiên, trong số các phương án phát triển khả thi, tôi chỉ chọn những phương án hiện đang được dễ sử dụng. Đối với mỗi mục, bạn có thư viện tạo sẵn hoặc bạn có cơ hội giải quyết vấn đề bằng nền tảng đám mây.

Chia tỷ lệ theo chiều ngang

Điểm đơn giản nhất và nổi tiếng nhất. Thông thường, hai sơ đồ phân phối tải phổ biến nhất là chia tỷ lệ theo chiều ngang và chiều dọc. Trong trường hợp đầu tiên bạn cho phép các dịch vụ chạy song song, từ đó phân phối tải giữa chúng. Thứ hai bạn yêu cầu các máy chủ mạnh hơn hoặc tối ưu hóa mã.

Ví dụ: tôi sẽ sử dụng bộ lưu trữ tệp đám mây trừu tượng, nghĩa là một số loại tương tự của OwnCloud, OneDrive, v.v.

Dưới đây là hình ảnh tiêu chuẩn của mạch như vậy, nhưng nó chỉ thể hiện độ phức tạp của hệ thống. Rốt cuộc, chúng ta cần phải đồng bộ hóa các dịch vụ bằng cách nào đó. Điều gì xảy ra nếu người dùng lưu một tập tin từ máy tính bảng và sau đó muốn xem nó từ điện thoại?

Mẫu kiến ​​trúc tiện lợi
Sự khác biệt giữa các phương pháp: trong chia tỷ lệ theo chiều dọc, chúng tôi sẵn sàng tăng sức mạnh của các nút và theo tỷ lệ theo chiều ngang, chúng tôi sẵn sàng thêm các nút mới để phân phối tải.

CQRS

Phân tách trách nhiệm truy vấn lệnh Một mẫu khá quan trọng, vì nó cho phép các máy khách khác nhau không chỉ kết nối với các dịch vụ khác nhau mà còn nhận được các luồng sự kiện giống nhau. Lợi ích của nó không quá rõ ràng đối với một ứng dụng đơn giản, nhưng nó cực kỳ quan trọng (và đơn giản) đối với một dịch vụ bận rộn. Bản chất của nó: các luồng dữ liệu đến và đi không được giao nhau. Nghĩa là, bạn không thể gửi yêu cầu và mong đợi phản hồi; thay vào đó, bạn gửi yêu cầu đến dịch vụ A nhưng nhận được phản hồi từ dịch vụ B.

Phần thưởng đầu tiên của phương pháp này là khả năng ngắt kết nối (theo nghĩa rộng của từ này) trong khi thực hiện một yêu cầu dài. Ví dụ: hãy lấy một chuỗi tiêu chuẩn ít nhiều:

  1. Client gửi yêu cầu tới server.
  2. Máy chủ bắt đầu xử lý lâu.
  3. Máy chủ đã phản hồi lại kết quả cho khách hàng.

Hãy tưởng tượng rằng ở điểm 2, kết nối bị hỏng (hoặc mạng được kết nối lại hoặc người dùng truy cập vào một trang khác, làm đứt kết nối). Trong trường hợp này, máy chủ sẽ khó gửi phản hồi cho người dùng kèm thông tin về chính xác những gì đã được xử lý. Khi sử dụng CQRS, trình tự sẽ hơi khác một chút:

  1. Khách hàng đã đăng ký nhận bản cập nhật.
  2. Client gửi yêu cầu tới server.
  3. Máy chủ đã phản hồi “yêu cầu được chấp nhận”.
  4. Máy chủ phản hồi kết quả thông qua kênh từ điểm “1”.

Mẫu kiến ​​trúc tiện lợi

Như bạn có thể thấy, sơ đồ phức tạp hơn một chút. Hơn nữa, cách tiếp cận đáp ứng yêu cầu trực quan bị thiếu ở đây. Tuy nhiên, như bạn có thể thấy, việc ngắt kết nối trong khi xử lý yêu cầu sẽ không dẫn đến lỗi. Hơn nữa, trên thực tế, nếu người dùng được kết nối với dịch vụ từ một số thiết bị (ví dụ: từ điện thoại di động và từ máy tính bảng), thì bạn có thể đảm bảo rằng phản hồi sẽ đến với cả hai thiết bị.

Điều thú vị là mã để xử lý tin nhắn đến trở nên giống nhau (không phải 100%) đối với cả các sự kiện chịu ảnh hưởng của chính khách hàng và đối với các sự kiện khác, bao gồm cả các sự kiện từ các khách hàng khác.

Tuy nhiên, trên thực tế, chúng tôi nhận được thêm một phần thưởng do thực tế là luồng một chiều có thể được xử lý theo kiểu chức năng (sử dụng RX và tương tự). Và đây đã là một điểm cộng lớn, vì về bản chất, ứng dụng có thể được tạo ra hoàn toàn phản ứng và cũng sử dụng cách tiếp cận chức năng. Đối với các chương trình béo, điều này có thể tiết kiệm đáng kể các nguồn lực hỗ trợ và phát triển.

Nếu chúng tôi kết hợp phương pháp này với tỷ lệ theo chiều ngang, thì phần thưởng là chúng tôi có khả năng gửi yêu cầu đến một máy chủ và nhận phản hồi từ máy chủ khác. Do đó, khách hàng có thể chọn dịch vụ thuận tiện cho mình và hệ thống bên trong vẫn có thể xử lý các sự kiện một cách chính xác.

Nguồn cung ứng sự kiện

Như bạn đã biết, một trong những đặc điểm chính của hệ thống phân tán là không có thời gian chung, phần quan trọng chung. Đối với một quy trình, bạn có thể thực hiện đồng bộ hóa (trên cùng một mutexes), trong đó bạn chắc chắn rằng không có ai khác đang thực thi mã này. Tuy nhiên, điều này nguy hiểm đối với hệ thống phân tán, vì nó sẽ yêu cầu chi phí cao và cũng sẽ làm mất đi vẻ đẹp của việc chia tỷ lệ - tất cả các thành phần sẽ vẫn chờ một.

Từ đây chúng ta nhận được một thực tế quan trọng - một hệ thống phân tán nhanh không thể được đồng bộ hóa, vì khi đó chúng ta sẽ giảm hiệu suất. Mặt khác, chúng ta thường cần sự nhất quán nhất định giữa các thành phần. Và để làm được điều này, bạn có thể sử dụng phương pháp tiếp cận với cuối cùng nhất quán, trong đó đảm bảo rằng nếu không có thay đổi dữ liệu nào trong một khoảng thời gian sau lần cập nhật cuối cùng (“cuối cùng”), thì tất cả các truy vấn sẽ trả về giá trị được cập nhật lần cuối.

Điều quan trọng là phải hiểu rằng đối với cơ sở dữ liệu cổ điển, nó thường được sử dụng tính nhất quán mạnh mẽ, trong đó mỗi nút có cùng thông tin (điều này thường đạt được trong trường hợp giao dịch được coi là chỉ được thiết lập sau khi máy chủ thứ hai phản hồi). Ở đây có một số cảm giác thư thái do mức độ biệt lập, nhưng ý tưởng chung vẫn như cũ - bạn có thể sống trong một thế giới hoàn toàn hài hòa.

Tuy nhiên, hãy quay lại nhiệm vụ ban đầu. Nếu một phần của hệ thống có thể được xây dựng bằng cuối cùng nhất quán, khi đó chúng ta có thể xây dựng sơ đồ sau.

Mẫu kiến ​​trúc tiện lợi

Các tính năng quan trọng của phương pháp này:

  • Mỗi yêu cầu đến được đặt trong một hàng đợi.
  • Trong khi xử lý yêu cầu, dịch vụ cũng có thể đặt các tác vụ vào hàng đợi khác.
  • Mỗi sự kiện đến có một mã định danh (cần thiết để loại bỏ trùng lặp).
  • Về mặt ý thức hệ, hàng đợi hoạt động theo sơ đồ “chỉ nối thêm”. Bạn không thể xóa các phần tử khỏi nó hoặc sắp xếp lại chúng.
  • Hàng đợi hoạt động theo sơ đồ FIFO (xin lỗi vì tautology). Nếu bạn cần thực hiện song song thì ở một giai đoạn, bạn nên di chuyển các đối tượng sang các hàng đợi khác nhau.

Hãy để tôi nhắc bạn rằng chúng tôi đang xem xét trường hợp lưu trữ tệp trực tuyến. Trong trường hợp này, hệ thống sẽ trông giống như thế này:

Mẫu kiến ​​trúc tiện lợi

Điều quan trọng là các dịch vụ trong sơ đồ không nhất thiết phải có một máy chủ riêng biệt. Ngay cả quá trình có thể giống nhau. Một điều quan trọng khác: về mặt ý thức hệ, những thứ này được tách biệt theo cách mà có thể dễ dàng áp dụng tỷ lệ theo chiều ngang.

Và đối với hai người dùng, sơ đồ sẽ như thế này (các dịch vụ dành cho những người dùng khác nhau được biểu thị bằng các màu khác nhau):

Mẫu kiến ​​trúc tiện lợi

Tiền thưởng từ sự kết hợp như vậy:

  • Dịch vụ xử lý thông tin được tách biệt. Các hàng đợi cũng được tách ra. Nếu cần tăng thông lượng hệ thống thì chúng tôi chỉ cần khởi chạy nhiều dịch vụ hơn trên nhiều máy chủ hơn.
  • Khi nhận được thông tin từ người dùng, chúng tôi không phải đợi cho đến khi dữ liệu được lưu hoàn toàn. Ngược lại, chúng ta chỉ cần trả lời “ok” rồi dần dần bắt đầu làm việc. Đồng thời, hàng đợi sẽ làm phẳng các đỉnh vì việc thêm một đối tượng mới diễn ra nhanh chóng và người dùng không phải đợi hoàn thành toàn bộ chu trình.
  • Ví dụ: tôi đã thêm một dịch vụ chống trùng lặp nhằm cố gắng hợp nhất các tệp giống hệt nhau. Nếu nó hoạt động trong thời gian dài trong 1% trường hợp, khách hàng sẽ khó nhận thấy nó (xem ở trên), đây là một điểm cộng lớn vì chúng tôi không còn yêu cầu tốc độ XNUMX% và đáng tin cậy nữa.

Tuy nhiên, những nhược điểm có thể nhìn thấy ngay:

  • Hệ thống của chúng tôi đã mất tính nhất quán nghiêm ngặt. Điều này có nghĩa là, chẳng hạn, nếu bạn đăng ký các dịch vụ khác nhau, thì về mặt lý thuyết, bạn có thể nhận được trạng thái khác (vì một trong các dịch vụ có thể không có thời gian để nhận thông báo từ hàng đợi nội bộ). Một hệ quả nữa là hệ thống hiện nay không có thời gian chung. Nghĩa là, chẳng hạn, không thể sắp xếp tất cả các sự kiện chỉ theo thời gian đến, vì đồng hồ giữa các máy chủ có thể không đồng bộ (hơn nữa, cùng một thời điểm trên hai máy chủ là điều không tưởng).
  • Bây giờ không có sự kiện nào có thể được khôi phục một cách đơn giản (như có thể được thực hiện với cơ sở dữ liệu). Thay vào đó, bạn cần thêm một sự kiện mới – sự kiện bồi thường, sẽ thay đổi trạng thái cuối cùng thành trạng thái được yêu cầu. Như một ví dụ từ một khu vực tương tự: không viết lại lịch sử (điều này không tốt trong một số trường hợp), bạn không thể khôi phục một cam kết trong git, nhưng bạn có thể tạo một cam kết đặc biệt cam kết khôi phục, về cơ bản chỉ trả về trạng thái cũ. Tuy nhiên, cả cam kết sai và khôi phục sẽ vẫn còn trong lịch sử.
  • Lược đồ dữ liệu có thể thay đổi từ bản phát hành này sang bản phát hành khác, nhưng các sự kiện cũ sẽ không thể cập nhật lên tiêu chuẩn mới nữa (vì về nguyên tắc các sự kiện không thể thay đổi).

Như bạn có thể thấy, Tìm nguồn cung ứng sự kiện hoạt động tốt với CQRS. Hơn nữa, việc triển khai một hệ thống với các hàng đợi hiệu quả và thuận tiện nhưng không tách biệt các luồng dữ liệu bản thân nó đã khó khăn vì bạn sẽ phải thêm các điểm đồng bộ hóa để vô hiệu hóa toàn bộ tác động tích cực của hàng đợi. Áp dụng cả hai phương pháp cùng một lúc, cần phải điều chỉnh một chút mã chương trình. Trong trường hợp của chúng tôi, khi gửi tệp đến máy chủ, phản hồi chỉ là “ok”, điều đó chỉ có nghĩa là “thao tác thêm tệp đã được lưu”. Về mặt hình thức, điều này không có nghĩa là dữ liệu đã có sẵn trên các thiết bị khác (ví dụ: dịch vụ chống trùng lặp có thể xây dựng lại chỉ mục). Tuy nhiên, sau một thời gian, khách hàng sẽ nhận được thông báo kiểu “file X đã được lưu”.

Kết quả là:

  • Số lượng trạng thái gửi tệp ngày càng tăng: thay vì “tệp đã gửi” cổ điển, chúng tôi nhận được hai trạng thái: “tệp đã được thêm vào hàng đợi trên máy chủ” và “tệp đã được lưu trong bộ lưu trữ”. Điều sau có nghĩa là các thiết bị khác đã có thể bắt đầu nhận tệp (được điều chỉnh vì thực tế là hàng đợi hoạt động ở các tốc độ khác nhau).
  • Do thông tin gửi hiện nay thông qua nhiều kênh khác nhau nên chúng ta cần đưa ra giải pháp để tiếp nhận trạng thái xử lý của file. Kết quả của việc này: không giống như phản hồi yêu cầu cổ điển, máy khách có thể được khởi động lại trong khi xử lý tệp, nhưng trạng thái của quá trình xử lý này sẽ chính xác. Hơn nữa, về cơ bản, mặt hàng này hoạt động ngay lập tức. Kết quả là: giờ đây chúng ta đã khoan dung hơn với những thất bại.

Sharding

Như đã mô tả ở trên, hệ thống tìm nguồn cung ứng sự kiện thiếu tính nhất quán nghiêm ngặt. Điều này có nghĩa là chúng ta có thể sử dụng nhiều kho lưu trữ mà không cần bất kỳ sự đồng bộ hóa nào giữa chúng. Tiếp cận vấn đề của chúng ta, chúng ta có thể:

  • Tách các tập tin theo loại. Ví dụ: hình ảnh/video có thể được giải mã và có thể chọn định dạng hiệu quả hơn.
  • Tách các tài khoản theo quốc gia. Do nhiều luật, điều này có thể được yêu cầu, nhưng sơ đồ kiến ​​trúc này tự động cung cấp cơ hội như vậy

Mẫu kiến ​​trúc tiện lợi

Nếu bạn muốn chuyển dữ liệu từ bộ lưu trữ này sang bộ lưu trữ khác thì các phương tiện tiêu chuẩn không còn đủ nữa. Thật không may, trong trường hợp này, bạn cần dừng hàng đợi, thực hiện di chuyển và sau đó bắt đầu nó. Trong trường hợp chung, dữ liệu không thể được truyền “nhanh chóng”, tuy nhiên, nếu hàng đợi sự kiện được lưu trữ hoàn toàn và bạn có ảnh chụp nhanh về trạng thái lưu trữ trước đó thì chúng ta có thể phát lại các sự kiện như sau:

  • Trong Nguồn sự kiện, mỗi sự kiện có mã định danh riêng (lý tưởng nhất là không giảm). Điều này có nghĩa là chúng ta có thể thêm một trường vào bộ lưu trữ - id của phần tử được xử lý cuối cùng.
  • Chúng tôi sao chép hàng đợi để tất cả các sự kiện có thể được xử lý cho một số kho lưu trữ độc lập (kho đầu tiên là kho lưu trữ dữ liệu đã được lưu trữ và kho thứ hai là kho mới nhưng vẫn trống). Tất nhiên, hàng đợi thứ hai vẫn chưa được xử lý.
  • Chúng tôi khởi chạy hàng đợi thứ hai (nghĩa là chúng tôi bắt đầu phát lại các sự kiện).
  • Khi hàng đợi mới tương đối trống (nghĩa là chênh lệch thời gian trung bình giữa việc thêm phần tử và truy xuất phần tử đó là có thể chấp nhận được), bạn có thể bắt đầu chuyển trình đọc sang bộ lưu trữ mới.

Như bạn có thể thấy, chúng tôi đã không có và vẫn chưa có sự nhất quán nghiêm ngặt trong hệ thống của mình. Chỉ có sự nhất quán cuối cùng, tức là sự đảm bảo rằng các sự kiện được xử lý theo cùng một thứ tự (nhưng có thể có độ trễ khác nhau). Và bằng cách sử dụng điều này, chúng ta có thể chuyển dữ liệu một cách tương đối dễ dàng mà không cần dừng hệ thống sang bên kia địa cầu.

Do đó, tiếp tục ví dụ của chúng tôi về lưu trữ trực tuyến cho các tệp, kiến ​​trúc như vậy đã mang lại cho chúng tôi một số lợi ích:

  • Chúng ta có thể di chuyển các đối tượng đến gần người dùng hơn một cách linh hoạt. Bằng cách này bạn có thể cải thiện chất lượng dịch vụ.
  • Chúng tôi có thể lưu trữ một số dữ liệu trong các công ty. Ví dụ: Người dùng Doanh nghiệp thường yêu cầu dữ liệu của họ được lưu trữ trong các trung tâm dữ liệu được kiểm soát (để tránh rò rỉ dữ liệu). Thông qua sharding chúng ta có thể dễ dàng hỗ trợ điều này. Và nhiệm vụ thậm chí còn dễ dàng hơn nếu khách hàng có đám mây tương thích (ví dụ: Azure tự lưu trữ).
  • Và điều quan trọng nhất là chúng ta không phải làm điều này. Xét cho cùng, trước hết, chúng tôi sẽ khá hài lòng với một bộ lưu trữ cho tất cả các tài khoản (để bắt đầu làm việc nhanh chóng). Và đặc điểm chính của hệ thống này là mặc dù có thể mở rộng nhưng ở giai đoạn đầu, nó khá đơn giản. Bạn không cần phải viết ngay mã hoạt động với hàng triệu hàng đợi độc lập riêng biệt, v.v. Nếu cần thiết, điều này có thể được thực hiện trong tương lai.

Lưu trữ nội dung tĩnh

Điểm này có vẻ khá rõ ràng, nhưng nó vẫn cần thiết đối với một ứng dụng được tải tiêu chuẩn ít nhiều. Bản chất của nó rất đơn giản: tất cả nội dung tĩnh được phân phối không phải từ cùng một máy chủ nơi đặt ứng dụng mà từ những nội dung đặc biệt dành riêng cho nhiệm vụ này. Kết quả là các thao tác này được thực hiện nhanh hơn (nginx có điều kiện phục vụ các tệp nhanh hơn và ít tốn kém hơn so với máy chủ Java). Cộng với kiến ​​trúc CDN (Mạng Phân phối Nội dung) cho phép chúng tôi định vị các tệp của mình gần hơn với người dùng cuối, điều này có tác động tích cực đến sự thuận tiện khi làm việc với dịch vụ.

Ví dụ đơn giản và tiêu chuẩn nhất về nội dung tĩnh là tập hợp các tập lệnh và hình ảnh cho một trang web. Mọi thứ đều đơn giản với chúng - chúng được biết trước, sau đó kho lưu trữ được tải lên máy chủ CDN, từ đó chúng được phân phối đến người dùng cuối.

Tuy nhiên, trên thực tế, đối với nội dung tĩnh, bạn có thể sử dụng cách tiếp cận tương tự như kiến ​​trúc lambda. Hãy quay lại nhiệm vụ của chúng ta (lưu trữ tệp trực tuyến), trong đó chúng ta cần phân phối tệp cho người dùng. Giải pháp đơn giản nhất là tạo một dịch vụ, đối với mỗi yêu cầu của người dùng, sẽ thực hiện tất cả các bước kiểm tra cần thiết (ủy quyền, v.v.), sau đó tải tệp trực tiếp xuống từ bộ lưu trữ của chúng tôi. Nhược điểm chính của phương pháp này là nội dung tĩnh (và một tệp có sửa đổi nhất định trên thực tế là nội dung tĩnh) được phân phối bởi cùng một máy chủ chứa logic nghiệp vụ. Thay vào đó, bạn có thể tạo sơ đồ sau:

  • Máy chủ cung cấp URL tải xuống. Nó có thể có dạng file_id + key, trong đó key là chữ ký số nhỏ cấp quyền truy cập tài nguyên trong XNUMX giờ tới.
  • Tệp được phân phối bởi nginx đơn giản với các tùy chọn sau:
    • Bộ nhớ đệm nội dung. Vì dịch vụ này có thể được đặt trên một máy chủ riêng biệt nên chúng tôi đã để lại cho mình một khoản dự trữ cho tương lai với khả năng lưu trữ tất cả các tệp được tải xuống mới nhất trên đĩa.
    • Kiểm tra key tại thời điểm tạo kết nối
  • Tùy chọn: xử lý nội dung trực tuyến. Ví dụ: nếu chúng tôi nén tất cả các tệp trong dịch vụ thì chúng tôi có thể giải nén trực tiếp trong mô-đun này. Kết quả là: các hoạt động IO được thực hiện ở nơi chúng thuộc về. Trình lưu trữ trong Java sẽ dễ dàng phân bổ nhiều bộ nhớ bổ sung, nhưng việc viết lại một dịch vụ có logic nghiệp vụ thành các điều kiện Rust/C++ cũng có thể không hiệu quả. Trong trường hợp của chúng tôi, các quy trình khác nhau (hoặc thậm chí các dịch vụ) được sử dụng và do đó chúng tôi có thể tách biệt khá hiệu quả logic nghiệp vụ và hoạt động IO.

Mẫu kiến ​​trúc tiện lợi

Sơ đồ này không giống lắm với việc phân phối nội dung tĩnh (vì chúng tôi không tải toàn bộ gói tĩnh lên đâu đó), nhưng trên thực tế, cách tiếp cận này liên quan chính xác đến việc phân phối dữ liệu bất biến. Hơn nữa, sơ đồ này có thể được khái quát hóa cho các trường hợp khác trong đó nội dung không chỉ đơn giản là tĩnh mà còn có thể được biểu diễn dưới dạng tập hợp các khối bất biến và không thể xóa được (mặc dù chúng có thể được thêm vào).

Một ví dụ khác (để củng cố): nếu bạn đã làm việc với Jenkins/TeamCity thì bạn biết rằng cả hai giải pháp đều được viết bằng Java. Cả hai đều là một quy trình Java xử lý cả việc điều phối xây dựng và quản lý nội dung. Cụ thể, cả hai đều có nhiệm vụ như “chuyển tập tin/thư mục từ máy chủ”. Ví dụ: phát hành các tạo phẩm, chuyển mã nguồn (khi tác nhân không tải mã trực tiếp từ kho lưu trữ mà máy chủ thực hiện việc đó cho anh ta), truy cập vào nhật ký. Tất cả các tác vụ này khác nhau về tải IO của chúng. Nghĩa là, hóa ra máy chủ chịu trách nhiệm về logic nghiệp vụ phức tạp phải đồng thời có khả năng đẩy các luồng dữ liệu lớn đi qua chính nó một cách hiệu quả. Và điều thú vị nhất là một thao tác như vậy có thể được ủy quyền cho cùng một nginx theo cùng một sơ đồ (ngoại trừ khóa dữ liệu phải được thêm vào yêu cầu).

Tuy nhiên, nếu quay lại hệ thống của mình, chúng ta sẽ nhận được một sơ đồ tương tự:

Mẫu kiến ​​trúc tiện lợi

Như bạn có thể thấy, hệ thống đã trở nên phức tạp hơn rất nhiều. Giờ đây, nó không chỉ là một quy trình nhỏ lưu trữ tệp cục bộ. Bây giờ những gì được yêu cầu không phải là sự hỗ trợ đơn giản nhất, kiểm soát phiên bản API, v.v. Vì vậy, sau khi vẽ xong tất cả các sơ đồ, tốt nhất bạn nên đánh giá chi tiết xem khả năng mở rộng có xứng đáng với chi phí bỏ ra hay không. Tuy nhiên, nếu bạn muốn có thể mở rộng hệ thống (bao gồm cả hoạt động với số lượng người dùng thậm chí còn lớn hơn), thì bạn sẽ phải tìm kiếm các giải pháp tương tự. Tuy nhiên, do đó, về mặt kiến ​​trúc, hệ thống đã sẵn sàng để tăng tải (hầu hết mọi thành phần đều có thể được sao chép để mở rộng quy mô theo chiều ngang). Hệ thống có thể được cập nhật mà không cần dừng (chỉ một số thao tác sẽ bị chậm lại một chút).

Như tôi đã nói lúc đầu, hiện nay một số dịch vụ Internet đã bắt đầu nhận được lượng tải tăng lên. Và một số trong số họ chỉ đơn giản là bắt đầu ngừng hoạt động bình thường. Trên thực tế, hệ thống đã thất bại ngay tại thời điểm doanh nghiệp lẽ ra phải kiếm tiền. Nghĩa là, thay vì hoãn giao hàng, thay vì gợi ý cho khách hàng “lên kế hoạch giao hàng trong những tháng tới”, hệ thống chỉ đơn giản nói “hãy đến gặp đối thủ cạnh tranh của bạn”. Trên thực tế, đây là cái giá của năng suất thấp: thua lỗ sẽ xảy ra chính xác khi lợi nhuận đạt mức cao nhất.

Kết luận

Tất cả những cách tiếp cận này đã được biết đến trước đây. VK tương tự từ lâu đã sử dụng ý tưởng Lưu trữ nội dung tĩnh để hiển thị hình ảnh. Rất nhiều trò chơi trực tuyến sử dụng sơ đồ Sharding để chia người chơi thành các khu vực hoặc tách các địa điểm trò chơi (nếu bản thân thế giới là một). Phương pháp Tìm nguồn cung ứng sự kiện được sử dụng tích cực trong email. Hầu hết các ứng dụng giao dịch nơi dữ liệu liên tục được nhận đều thực sự được xây dựng theo phương pháp CQRS để có thể lọc dữ liệu nhận được. Chà, chia tỷ lệ theo chiều ngang đã được sử dụng trong nhiều dịch vụ trong một thời gian khá dài.

Tuy nhiên, quan trọng nhất, tất cả các mẫu này đã trở nên rất dễ áp ​​dụng trong các ứng dụng hiện đại (tất nhiên là nếu chúng phù hợp). Đám mây cung cấp tính năng Sharding và chia tỷ lệ theo chiều ngang ngay lập tức, điều này dễ dàng hơn nhiều so với việc tự mình đặt hàng các máy chủ chuyên dụng khác nhau ở các trung tâm dữ liệu khác nhau. CQRS đã trở nên dễ dàng hơn nhiều nhờ sự phát triển của các thư viện như RX. Khoảng 10 năm trước, hiếm có website nào có thể hỗ trợ được điều này. Tìm nguồn cung ứng sự kiện cũng cực kỳ dễ thiết lập nhờ các vùng chứa làm sẵn với Apache Kafka. 10 năm trước đây có thể là một sự đổi mới, nhưng bây giờ nó đã trở nên phổ biến. Điều này cũng tương tự với Lưu trữ nội dung tĩnh: nhờ có công nghệ tiện lợi hơn (bao gồm cả thực tế là có tài liệu chi tiết và cơ sở dữ liệu câu trả lời lớn), cách tiếp cận này thậm chí còn trở nên đơn giản hơn.

Kết quả là, việc thực hiện một số mẫu kiến ​​trúc khá phức tạp giờ đây đã trở nên đơn giản hơn nhiều, điều đó có nghĩa là tốt hơn hết bạn nên xem xét kỹ hơn trước. Nếu trong một ứng dụng đã mười năm tuổi, một trong những giải pháp trên đã bị loại bỏ do chi phí triển khai và vận hành cao thì bây giờ, trong một ứng dụng mới hoặc sau khi tái cấu trúc, bạn có thể tạo một dịch vụ có thể mở rộng cả về mặt kiến ​​trúc ( về mặt hiệu suất) và sẵn sàng đáp ứng các yêu cầu mới từ khách hàng (ví dụ: để bản địa hóa dữ liệu cá nhân).

Và quan trọng nhất: vui lòng không sử dụng những cách tiếp cận này nếu bạn có một ứng dụng đơn giản. Đúng, chúng rất đẹp và thú vị, nhưng đối với một trang web có lượng truy cập cao nhất là 100 người, bạn thường có thể sử dụng một khối nguyên khối cổ điển (ít nhất là ở bên ngoài, mọi thứ bên trong có thể được chia thành các mô-đun, v.v.).

Nguồn: www.habr.com

Thêm một lời nhận xét