by Marcia Villalba | ngày 14 tháng 9 năm 2022 | trong Amazon CloudFront, Amazon Cognito, Amazon Simple Storage Service (S3), AWS Amplify, AWS Lambda, Serverless | Permalink | Share
Trong phần 1, bạn đã học liệu có thể di chuyển một ứng dụng web không phải serverless sang môi trường serverless mà không cần thay đổi nhiều mã nguồn hay không. Bạn đã học về các công cụ khác nhau có thể giúp bạn trong quá trình này, như Lambda Web Adaptor và AWS Amplify. Cuối cùng, bạn đã di chuyển được một ứng dụng vào môi trường serverless.
Tuy nhiên, khi kiểm tra ứng dụng đã được di chuyển, bạn phát hiện ra hai vấn đề. Vấn đề đầu tiên là phiên người dùng không ổn định. Mỗi khi bạn đăng nhập, bạn sẽ bị đăng xuất bất ngờ khỏi ứng dụng. Vấn đề thứ hai là khi bạn tạo một sản phẩm mới, bạn không thể tải lên hình ảnh mới của sản phẩm đó.
Bài viết cuối cùng này sẽ phân tích chi tiết từng vấn đề và đưa ra giải pháp. Ngoài ra, nó còn phân tích chi phí và hiệu suất của giải pháp.
Di chuyển xác thực và ủy quyền
Ứng dụng gốc xử lý xác thực và ủy quyền tự nó. Có một thư mục người dùng trong cơ sở dữ liệu, với mật khẩu và email của từng người dùng. Có các API và middleware chịu trách nhiệm xác thực rằng người dùng đã đăng nhập trước khi hiển thị ứng dụng. Tất cả logic này được phát triển bên trong ứng dụng Node.js/Express.
Tuy nhiên, với ứng dụng đã di chuyển hiện tại, mỗi khi bạn đăng nhập, bạn sẽ bị đăng xuất bất ngờ khỏi ứng dụng. Điều này xảy ra vì mã server chịu trách nhiệm xử lý xác thực và ủy quyền người dùng, và bây giờ server của chúng ta đang chạy trong một AWS Lambda function và các function là stateless. Điều này có nghĩa là sẽ có một function chạy mỗi lần yêu cầu—một yêu cầu có thể tải tất cả các sản phẩm trên trang landing, lấy chi tiết cho một sản phẩm, hoặc đăng nhập vào trang web—và nếu bạn làm gì đó trong một trong những function này, trạng thái sẽ không được chia sẻ giữa các function.
Để giải quyết vấn đề này, bạn phải loại bỏ các cơ chế xác thực và ủy quyền từ function và sử dụng một dịch vụ có thể bảo toàn trạng thái qua nhiều lần gọi function.
Có nhiều cách để giải quyết thách thức này. Bạn có thể thêm một lớp xác thực và quản lý phiên với một cơ sở dữ liệu như Redis, hoặc xây dựng một microservice mới chịu trách nhiệm về xác thực và ủy quyền có thể xử lý trạng thái, hoặc sử dụng một dịch vụ managed hiện có cho việc này.
Vì yêu cầu di chuyển, chúng ta muốn giữ chi phí thấp nhất có thể, với ít thay đổi nhất cho ứng dụng. Giải pháp tốt hơn là sử dụng một dịch vụ managed hiện có để xử lý xác thực và ủy quyền.
Bản demo này sử dụng Amazon Cognito, cung cấp xác thực và ủy quyền người dùng cho các tài nguyên AWS theo cách managed và pay-as-you-go. Một cách tiếp cận nhanh là thay thế tất cả mã server bằng các cuộc gọi đến Amazon Cognito sử dụng AWS SDK. Nhưng điều này thêm phức tạp mà có thể được thay thế hoàn toàn bằng cách chỉ gọi Amazon Cognito API từ ứng dụng React.

Sử dụng Cognito
Ví dụ, khi một người dùng mới đăng ký, ứng dụng sẽ tạo người dùng trong thư mục user pool của Amazon Cognito, cũng như trong cơ sở dữ liệu của ứng dụng. Nhưng khi một người dùng đăng nhập vào ứng dụng web, ứng dụng sẽ gọi API Amazon Cognito trực tiếp từ ứng dụng AWS Amplify. Cách này tối thiểu hóa lượng mã cần thiết.
Trong ứng dụng gốc, tất cả các API server được xác thực đều được bảo mật bằng một middleware xác thực rằng người dùng đã xác thực bằng cách cung cấp một access token. Với cài đặt mới, điều này không thay đổi, nhưng token được tạo bởi Amazon Cognito và sau đó có thể được xác thực ở backend.
let auth = (req, res, next) => {
const token = req.headers.authorization;
const jwtToken = token.replace('Bearer ', '');
verifyToken(jwtToken)
.then((valid) => {
if (valid) {
getCognitoUser(jwtToken).then((email) => {
User.findByEmail(email, (err, user) => {
if (err) throw err;
if (!user)
return res.json({
isAuth: false,
error: true,
});
req.user = user;
next();
});
});
} else {
throw Error('Not valid Token');
}
})
.catch((error) => {
return res.json({
isAuth: false,
error: true,
});
});
};
Bạn có thể thấy cách này được triển khai từng bước trong video này.
Di chuyển lưu trữ
Trong ứng dụng gốc, khi một sản phẩm mới được tạo, một hình ảnh mới được tải lên server Node.js/Express. Tuy nhiên, bây giờ ứng dụng nằm trong một Lambda function. Mã (và các file) là một phần của function đó không thể thay đổi, trừ khi function được triển khai lại. Do đó, bạn phải tách việc lưu trữ người dùng khỏi mã server.
Để làm điều này, có một số giải pháp: sử dụng Amazon Elastic File System (EFS) hoặc Amazon S3. EFS là một hệ thống lưu trữ file, và bạn có thể sử dụng nó để có một lưu trữ động nơi bạn tải lên các hình ảnh mới. Sử dụng EFS sẽ không thay đổi nhiều mã, vì triển khai gốc sử dụng một thư mục bên trong server giống như EFS cung cấp. Tuy nhiên, sử dụng EFS thêm phức tạp cho ứng dụng, vì các function sử dụng EFS phải nằm trong một Amazon Virtual Private Cloud (Amazon VPC).
Sử dụng S3 để tải hình ảnh lên ứng dụng đơn giản hơn, vì chỉ yêu cầu có một S3 bucket. Để làm điều này, bạn phải refactor ứng dụng, từ việc tải lên hình ảnh đến API ứng dụng để sử dụng thư viện AWS Amplify tải lên và lấy hình ảnh từ S3.
export function uploadImage(file) {
const fileName = `uploads/${file.name}`;
const request = Storage.put(fileName, file).then((result) => {
return {
image: fileName,
success: true,
};
});
return {
type: IMAGE_UPLOAD,
payload: request,
};
}
Một lợi ích quan trọng của việc sử dụng S3 là bạn cũng có thể sử dụng Amazon CloudFront để tăng tốc độ truy xuất hình ảnh từ đám mây. Bằng cách này, bạn có thể tăng tốc độ tải trang của mình. Bạn có thể thấy cách này được triển khai từng bước trong video này.
Chi phí của ứng dụng này là bao nhiêu?
Nếu bạn triển khai ứng dụng này trong một tài khoản AWS trống, hầu hết việc sử dụng ứng dụng này được bao phủ bởi AWS Free Tier. Các dịch vụ serverless, như Lambda và Amazon Cognito, có một free tier vĩnh viễn mang lại lợi ích về giá cả trong suốt thời gian lưu trữ ứng dụng.
- AWS Lambda—Với 100 yêu cầu mỗi giờ, một lần gọi trung bình 10ms và cấu hình bộ nhớ 1GB, chi phí là 0 USD mỗi tháng.
- Amazon S3—Sử dụng S3 standard, lưu trữ 1 GB mỗi tháng và 10k yêu cầu PUT và GET mỗi tháng có chi phí 0.07 USD mỗi tháng. Điều này có thể được tối ưu hóa bằng cách sử dụng S3 Intelligent-Tiering.
- Amazon Cognito—Cung cấp 50,000 người dùng hoạt động hàng tháng miễn phí.
- AWS Amplify—Nếu bạn xây dựng ứng dụng client một lần mỗi tuần, phục vụ 3 GB và lưu trữ 1 GB mỗi tháng, chi phí là 0.87 USD.
- AWS Secrets Manager—Có hai bí mật được lưu trữ bằng Secrets Manager và chi phí này là 1.16 USD mỗi tháng. Điều này có thể được tối ưu hóa bằng cách sử dụng AWS Systems Manager Parameter Store và AWS Key Management Service (AWS KMS).
- MongoDB Atlas—Cụm chia sẻ miễn phí vĩnh viễn.
Tổng chi phí hàng tháng của ứng dụng này là khoảng 2.11 USD.
Phân tích hiệu suất
Sau khi bạn di chuyển ứng dụng, bạn có thể chạy công cụ đo tốc độ trang để đo lường hiệu suất của ứng dụng này. Công cụ này cung cấp kết quả chủ yếu về front end và trải nghiệm mà người dùng cảm nhận. Kết quả được hiển thị trong hình sau. Hiệu suất của trang web này là tốt, theo điểm số hiệu suất của công cụ—nó phản hồi nhanh và trải nghiệm người dùng là tốt.

Sau khi ứng dụng được di chuyển vào môi trường serverless, bạn có thể thực hiện một số refactor để cải thiện hiệu suất tổng thể. Một giải pháp là bất cứ khi nào một hình ảnh mới được tải lên, nó sẽ được thay đổi kích thước và định dạng vào định dạng tiếp theo tự động sử dụng khả năng event-driven mà S3 cung cấp. Một giải pháp khác là sử dụng Lambda@Edge để phục vụ đúng kích thước hình ảnh cho thiết bị, vì có thể định dạng hình ảnh ngay khi phục vụ chúng từ một phân phối.
Bạn có thể chạy các bài kiểm tra tải để hiểu cách backend và cơ sở dữ liệu của bạn sẽ hoạt động. Để làm điều này, bạn có thể sử dụng Artillery, một thư viện mã nguồn mở cho phép bạn chạy các bài kiểm tra tải. Bạn có thể chạy các bài kiểm tra với tải tối đa dự kiến trang web của bạn sẽ nhận và đảm bảo rằng trang web của bạn có thể xử lý nó.
Ví dụ, bạn có thể cấu hình một bài kiểm tra gửi 30 yêu cầu mỗi giây để xem ứng dụng của bạn phản ứng thế nào:
config:
target: 'https://xxx.lambda-url.eu-west-1.on.aws'
phases:
- duration: 240
arrivalRate: 30
name: Testing
scenarios:
- name: 'Test main page'
flow:
- post:
url: '/api/product/getProducts/'
Bài kiểm tra này được thực hiện trên các API backend, không chỉ kiểm tra backend của bạn mà còn cả tích hợp của bạn với MongoDB. Sau khi chạy, bạn có thể thấy Lambda function hoạt động thế nào trên bảng điều khiển Amazon CloudWatch.
Chạy bài kiểm tra tải này giúp bạn hiểu được giới hạn của hệ thống của bạn. Ví dụ, nếu bạn chạy một bài kiểm tra với quá nhiều người dùng đồng thời, bạn có thể thấy số lần throttles trong function của bạn tăng lên. Điều này có nghĩa là bạn cần nâng giới hạn của số lần gọi function mà bạn có thể có cùng một lúc.
Hoặc khi tăng yêu cầu mỗi giây, bạn có thể thấy cụm MongoDB bắt đầu hạn chế các yêu cầu của bạn. Điều này là vì bạn đang sử dụng free tier và nó có một số kết nối nhất định. Bạn có thể cần một cụm lớn hơn hoặc di chuyển cơ sở dữ liệu của bạn sang một dịch vụ khác cung cấp một free tier lớn, như Amazon DynamoDB.

Kết luận
Trong bài viết hai phần này, bạn đã học liệu có thể di chuyển một ứng dụng web không phải serverless sang môi trường serverless mà không cần thay đổi nhiều mã nguồn hay không. Bạn đã học về các công cụ khác nhau có thể giúp bạn trong quá trình này, như AWS Lambda Web Adaptor và AWS Amplify, và cách giải quyết một số thách thức điển hình mà chúng ta gặp phải, như lưu trữ và xác thực.
Sau khi ứng dụng được lưu trữ trong một môi trường serverless hoàn toàn, nó có thể mở rộng lên và xuống để đáp ứng nhu cầu của bạn. Ứng dụng web này cũng có hiệu suất tốt khi backend được lưu trữ trong một Lambda function.
Nếu cần, từ đây bạn có thể bắt đầu sử dụng mô hình strangler để refactor ứng dụng để tận dụng lợi ích của kiến trúc event-driven.
Để xem tất cả các bước di chuyển, có một playlist chứa tất cả các hướng dẫn cho bạn theo dõi.
Để biết thêm tài nguyên học tập serverless, hãy truy cập Serverless Land.
TAGS: serverless