Server-side rendering micro-frontends – Trình tạo giao diện người dùng và khám phá dịch vụ

Đường dẫn bài viết gốc:

bởi James Beswick | ngày 09 tháng 3 năm 2023 |  AWS Fargate, AWS Lambda, Serverless | Permalink |  Share

Bài viết này được viết bởi Luca Mezzalira (Principal Specialist Solutions Architect, Serverless).

Bài viết trước đây previous blog post đã mô tả kiến trúc để tạo giao diện server-side rendering micro-frontend trên AWS. Bài viết này và các bài viết tiếp theo giải thích chi tiết các phần khác nhau tạo nên kiến trúc đó. Code của ví dụ có sẵn trên AWS Samples GitHub repository.

Về ngữ cảnh, bài đăng này đề cập đến cơ sở hạ tầng liên quan đến trình tạo giao diện người dùng và lý do tại sao bạn cần bộ chứa Amazon S3 để lưu trữ nội dung tĩnh:

Phần còn lại của loạt bài này sẽ  khám phá các thành phần của micro-frontends, cách thiết kế micro-frontends bằng cách sử dụng các dịch vụ serverless, các chiến lược tối ưu hóa hiệu suất và bộ nhớ đệm khác nhau cũng như ý nghĩa của cấu trúc tổ chức liên quan đến các hệ thống phân tán giao diện người dùng frontend.

Một hành trình yêu cầu của người dùng

Cách tốt nhất để chuyển qua hệ thống phân tán này là việc mô phỏng một yêu cầu của người dùng liên quan đến tất cả các phần được triển khai trong kiến trúc.

Ví dụ ứng dụng hiển thị trang chi tiết sản phẩm của nền tảng thương mại điện tử giả định:

Khi người dùng chọn một bài từ trang danh mục, DNS sẽ phân giải URL tới Amazon CloudFront distribution (là CDN tham chiếu cho dự án này).

Yêu cầu sẽ được thực hiện ngay lập tức nếu trang đã lưu vào bộ nhớ đệm. Do đó, không có logic bổ sung được yêu cầu bởi cơ sở hạ tầng đám mây và phản hồi là nhanh (dưới 500 mili giây được hiển thị trong ví dụ này).

Khi trang không có sẵn trong các CloudFront points of presence (PoPs), yêu cầu sẽ được chuyển tiếp đến Application Load Balancer (ALB). Nó đến AWS Fargate cluster nơi Trình tạo giao diện người dùng (UI Compose) sẽ tạo trang để thực hiện yêu cầu.

Sử dụng CloudFront trong kiến trúc

CDN được biết đến với khả năng tăng tốc điều phối ứng dụng nhờ lưu trữ các tệp tĩnh từ các PoP gần đó. CloudFront cũng có thể tăng tốc nội dung không thể lưu vào bộ nhớ đệm, chẳng hạn như các API động hoặc nội dung được cá nhân hóa.

Với mạng lưới hơn 450 points of presence, CloudFront sẽ chấm dứt các kết nối TCP/TLS của người dùng trong vòng trung bình 20-30 mili giây. Lưu lượng truy cập đến máy chủ nguồn được truyền thông qua mạng toàn cầu AWS thay vì Internet công cộng. Cơ sở hạ tầng này là cơ sở hạ tầng riêng biệt, được xây dựng có mục đích, có tính sẵn sàng cao và độ trễ thấp vì được xây dựng trên nền tảng cơ sở hạ tầng toàn cầu, dự phòng đầy đủ, mạng lưới cáp quang metro được liên kết thông qua các tuyến cáp mặt đất và xuyên đại dương trên toàn thế giới. Ngoài việc chấm dứt các kết nối gần người dùng, CloudFront còn tăng tốc nội dung động nhờ các giao thức Internet hiện đại như QUIC và TLS1.3 cũng như duy trì kết nối TCP với máy chủ nguồn.

CloudFront cũng có các lợi ích bảo mật, cung cấp khả năng bảo vệ trong AWS trước các cuộc tấn công cơ sở hạ tầng DDoS. Nó sẽ tích hợp với AWS Web Application FirewallAWS Shield Advanced, cung cấp cho bạn các quyền kiểm soát để chặn các cuộc tấn công DDoS ở cấp độ ứng dụng. CloudFront cũng cung cấp các biện pháp kiểm soát bảo mật cơ bản như chuyển hướng HTTP sang HTTPS, quản lý CORS, geo-blocking, mã hóa và quản lý security response headers.

Logic ứng dụng trình tạo giao diện người dùng

Khi yêu cầu không được thực hiện bởi bộ nhớ đệm CloudFront, thi yêu cầu đó sẽ được chuyển đến cụm Fargate. Ở đây, nhiều tác vụ sẽ tính toán và phục vụ trang được yêu cầu.

Ví dụ này sử dụng Fastify, một framework fast Node.js đang trở nên phổ biến trong cộng đồng Node.js. Khi máy chủ web khởi tạo, nó sẽ tải các tham số bên ngoài và mẫu để soạn trang.

const start = async () => {

  try {

    //load parameters

    MFElist = await init();

    //load catalog template

    catalogTemplate = await loadFromS3(MFElist.template, MFElist.templatesBucket)

    await fastify.listen({ port: PORT, host: ‘0.0.0.0’ })

  } catch (err) {

    fastify.log.error(err)

    process.exit(1)

  }

}

Để duy trì sự độc lập của nhóm và tránh việc triển khai lại trình tạo giao diện người dùng cho mỗi thay đổi của ứng dụng, thì các mẫu HTML được tải từ S3 bucket. Tất cả các nhóm chịu trách nhiệm về micro-frontends trong cùng một trang có thể đặt micro-frontends của họ vào đúng vị trí của mẫu HTML và giao nhiệm vụ sáng tác cho trình tạo giao diện người dùng.

Trong bản demo này, các tham số ban đầu và mẫu danh mục được truy xuất một lần. Tuy nhiên, trong trường hợp thực tế, nhiều khả năng bạn sẽ truy xuất các tham số khi khởi tạo và vào một thời điểm đều đặn. Mẫu có thể được tải vào thời gian chạy cho mỗi yêu cầu hoặc có một quy trình nền khác tìm nạp các tham số khởi tạo theo cách tương tự.

Khi yêu cầu đến đường dẫn chi tiết sản phẩm, logic ứng dụng web sẽ gọi hàm transformtemplate. Nó sẽ chuyển mẫu danh mục được lấy từ S3 bucket vào thời điểm khởi tạo máy chủ. Hàm trả về phản hồi 200 nếu trang được tạo mà không gặp vấn đề gì.

fastify.get(‘/productdetails’, async(request, reply) => {

  try{

    const catalogDetailspage = await transformTemplate(catalogTemplate)

    responseStream(catalogDetailspage, 200, reply)

  } catch(err){

    console.log(err)

    throw new Error(err)

  }

})

Bố cục trang là trách nhiệm chính của trình tạo giao diện người dùng. Có một số cách tiếp cận khả thi để tạo các giao diện người dùng micro-frontends trong hệ thống server-side rendering, điều này sẽ được đề cập trong bài đăng tiếp theo.

Khám phá Micro-frontends

Để tách biệt các khối lượng công việc cho nhiều nhóm, bạn phải sử dụng các mẫu kiến trúc hỗ trợ điều đó. Trong kiến trúc microservices, service discovery patter là một mẫu cho phép phát triển dịch vụ độc lập mà không cần liên  DNS hoặc IP với bất kỳ microservices nào.

Trong ví dụ này, AWS System Managers Parameters Store hoạt động như một bản đăng ký dịch vụ. Mỗi micro-frontend có sẵn trong khối lượng công việc sẽ tự đăng ký sau khi cơ sở hạ tầng được cung cấp.

Bằng cách này, trình tạo giao diện người dùng có thể yêu cầu micro-frontend ID được tìm thấy trong mẫu HTML. Ví dụ: nó có thể truy xuất một cách chính xác để sử dụng micro-frontend API bằng cách sử dụng ARN hoặc HTTP URL từ xa.

Việc sử dụng ARN thay vì các yêu cầu HTTP bên trong mạng workload có thể giúp bạn giảm độ trễ nhờ có ít bước nhảy mạng hơn. Hơn nữa, tính bảo mật được ủy quyền cho các IAM policies nhằm cung cấp khả năng triển khai bảo mật mạnh mẽ.

Trình tạo giao diện người dùng sẽ cẩn thận truy xuất các điểm cuối của micro-frontends trong thời gian chạy trước khi tải chúng vào mẫu HTML. Đây là một cách tiếp cận đơn giản nhưng mạnh mẽ hơn để duy trì các ranh giới trong tổ chức của bạn và cho phép các nhóm độc lập phát triển kiến trúc của họ một cách tự chủ.

Sự phát triển của việc khám phá các micro-frontends

Bằng cách sử dụng Parameter Store như một hệ thống khám phá dịch vụ, bạn có thể triển khai micro-frontend mới bằng cách thêm một cặp key-value mới vào hệ thống.

Một tùy chọn phức tạp hơn là bạn có thể tạo một dịch vụ hoạt động như một cơ quan đăng ký và cũng định hình lưu lượng truy cập tới các phiên bản micro-frontends khác nhau bằng cách sử dụng các chiến lược triển khai như canary releases hoặc blue/green deployments.

Bạn có thể bắt đầu theo cách tiếp cận một hệ thống lưu trữ key-value đơn giản và phát triển kiến trúc theo cách tiếp cận phức tạp hơn khi tăng khối lượng công việc, cung cấp một cách thức mạnh mẽ để triển khai các dịch vụ micro-frontends trong hệ thống của bạn.

Khi điều này được thiết lập, nó có khả năng làm tăng tốc độ phát hành của các micro-frontends của bạn. Điều này là do các nhà phát triển thường cảm thấy an toàn hơn khi phát hành phiên bản chính thức mà không ảnh hưởng đến toàn bộ người dùng và họ có thể chạy thử nghiệm cùng với lưu lượng truy cập thực sự.

Cân nhắc về hiệu suất

Kiến trúc này sử dụng Fargate để tạo các micro-frontends thay vì các Lambda functions. Điều này cho phép incremental rendering do trình duyệt cung cấp, hiển thị một phần trang HTML trước khi nó được trả về hoàn toàn.

Hãy xem xét trường hợp mà micro-frontends mất nhiều thời gian hơn để render do sự phụ thuộc phía dưới hoặc một phiên bản bị lỗi được triển khai vào môi trường sản xuất. Nếu không có khả năng streaming, bạn phải đợi cho đến khi tất cả các phản hồi của micro-frontends đến, lưu chúng vào bộ nhớ, tạo trang rồi gửi kết quả cuối cùng tới trình duyệt.

Thay vào đó, bằng cách sử dụng streaming API do Node.js frameworks cung cấp, bạn có thể gửi một phần trang HTML (ví dụ: thẻ head và sau đó là phần còn lại của trang) để được trình duyệt hiển thị.

Streaming cũng cải thiện chi phí hoạt động của máy chủ vì máy chủ không cầm đệm toàn bộ trang. Bằng cách chuyển dữ liệu tăng dần lên trình duyệt, máy chủ sẽ duy trì được áp lực bộ nhớ ở mức thấp, cho phép chúng xử lý nhiều yêu cầu hơn và tiết kiệm chi phí overhead.

Tuy nhiên, trong trường hợp khối lượng công việc của bạn không yêu cầu những khả năng này thì một hoặc nhiều Lambda function cũng có thể phù hợp với dự án của bạn, giúp giảm độ phức tạp trong việc quản lý cơ sở hạ tầng cần xử lý.

Kết luận

Bài viết này xem xét cách sử dụng trình tạo giao diện người dùng và khả năng khám phá các micro-frontends. Khi phần này được phát triển, nó sẽ không cần phải thay đổi thường xuyên. Điều này thể hiện nền tảng để xây dựng các micro-frontends hiển thị phía máy chủ bằng cách sử dụng HTML qua mạng. Có thể có các cách tiếp cận khác để áp dụng cho các framework khác như Next.js do việc triển khai kiến trúc của chính framework đó.

Bài viết tiếp theo sẽ đề cập đến cách trình tạo UI bao gồm đầu ra micro-frontends bên trong mẫu HTML

Để biết thêm tài nguyên học tập về serverless, hãy truy cập Serverless Land.

Leave a comment