Lambda Internals - Phần 2: Đi sâu hơn

Khám phá thư viện thời gian chạy AWS Lambda

Ảnh của Jim Beaudoin

Serverless phát triển đơn giản là tốt nhất. Nhấp đúp chuột, tải lên mã của bạn và bạn đã hoàn tất, phải không? Hầu hết mọi người đều hạnh phúc hơn khi để nó ở đó. Nếu bạn không phải là hầu hết mọi người, và trong một số khám phá Lambda, bài viết này chỉ dành cho bạn.

Trong bài viết trước, chúng ta đã có một shell cho container Lambda, tải xuống môi trường thời gian chạy Lambda và phát hiện ra các thành phần của nó:

  • bootstrap.py - mã python gói xử lý của chúng tôi.
  • awslambda / runtime.so - một đối tượng chia sẻ tương thích python bootstrap.py sử dụng nó cho tất cả mọi thứ.
  • liblambda * .so - Đến lượt mình, runtime.so sử dụng các đối tượng chia sẻ khác. Chúng tôi sẽ tập trung vào liblambdar nb.so, phụ trách công việc nặng nề trong việc quản lý logic Lambda.

Chúng tôi cũng đã có một số điều thú vị xung quanh với bootstrap.py. Lần này chúng ta sẽ xắn tay áo và đi sâu vào các thư viện nhị phân của môi trường thời gian chạy Lambda. Chúng tôi sẽ khám phá hệ thống thanh toán Lambda, và (cảnh báo spoiler) có một số điều thú vị với thời gian chờ của Lambda.

Mùi ơi, những nơi bạn sẽ đi! Có niềm vui để được thực hiện! Có những điểm được ghi. Có những trò chơi để chiến thắng. Hãy - Tiến sĩ Seuss. Ảnh của Joshua Earle

Khám phá các thư viện

Các thư viện (liblambda * .so) được biên dịch với các ký hiệu, vì vậy bạn có thể tìm hiểu rất nhiều về các thư viện chỉ bằng cách đi qua các tên ký hiệu. Ngoài ra, runtime.so hiển thị rất nhiều các hàm này bằng cách nhập và gói chúng, do đó, một tập lệnh Python (bootstrap.py trong trường hợp của chúng tôi) có thể sử dụng một số trong số chúng. Tiện như thế nào!

Danh sách các chức năng một phần từ việc tháo gỡ liblambdar nb.so. Cảm ơn chúa vì những biểu tượng.

Một trong những điều ban đầu tôi thực sự muốn kiểm tra là hậu trường của hệ thống thanh toán Lambda, và chỉ cần nhìn vào tên hàm, tôi đã có một số thử nghiệm tôi muốn thử. Nhưng trước tiên - hãy để một chút nói về thanh toán Lambda.

Thanh toán Lambda

Lambda có một mô hình định giá dựa trên thời gian và không đi sâu vào tất cả các chi tiết, ý chính của nó là Lambda của bạn chạy càng lâu, bạn càng phải trả nhiều tiền. Khi gọi Lambda, bạn có thể dễ dàng phát hiện điểm bắt đầu và kết thúc của nó trong Nhật ký CloudWatch, cũng như thời lượng và thời lượng thanh toán của nó.

Nhật ký CloudWatch cho Lambda. Bạn có thể thấy cả thời lượng Lambda và thời lượng hóa đơn

Tuy nhiên, có một kịch bản phức tạp hơn. Hãy xem xét Lambda sau đây:

Trong một lần chạy thông thường, thời lượng của Lambda này phải nhỏ (thời lượng hóa đơn hầu như luôn luôn là 100 ms). Nhưng điều gì xảy ra trong lần gọi đầu tiên? Hoặc khi bắt đầu lạnh (nơi mô-đun được nhập lại)?

Lambda đăng nhập khi một khởi đầu lạnh xảy ra. Thời lượng cao hơn nhiều so với một lời mời thông thường

Các thử nghiệm thực nghiệm cho thấy thời lượng của lần gọi Lambda đầu tiên (hoặc bắt đầu lạnh) chứa thời lượng khởi tạo. Nhưng tôi muốn kiểm tra xem Lambda thực hiện việc này như thế nào.

Nhập thư viện

Trong bootstrap.py, có các lệnh gọi đến các hàm sau, được nhập từ các thư viện nhị phân:

  • lambda_r nb.receive_start () hoặc lambda_r nb.receive_invoke () - khi nhận được một kích hoạt mới.
  • lambda_r nb.report_done () bất cứ khi nào một Lambda được thực hiện

Bây giờ có thể là thời điểm tốt để cung cấp thêm một số chi tiết về máy thái mà tôi đã đề cập trong bài viết trước. Máy thái là thành phần trong Lambda chịu trách nhiệm phân bổ thời gian chạy cho người dùng Lambdas khác nhau, chạy trên container. Các hàm này gửi thông báo đến bộ xử lý (và các thành phần quản lý Lambda khác) khi thực thi Lambda được thực hiện hoặc nhận thông tin về các lần thực hiện mới được bắt đầu.

Vì vậy, sau khi chúng tôi xác định các cuộc gọi từ lambda_r Yoon và biết máy cắt là gì, có một thứ tôi chỉ cần thử: tự mình nhập thư viện thời gian chạy và vui vẻ với nó! (những thí nghiệm này là cách tôi tìm ra thứ trên máy thái, chủ yếu bằng cách đọc phần tháo gỡ và một số thử nghiệm và lỗi). Bài kiểm tra tôi muốn chia sẻ với bạn cũng là bài kiểm tra đầu tiên tôi đã thử: gọi lambda_r Yoon.report_done () từ bên trong Lambda của tôi. Đây là mã tôi đã sử dụng:

Điều đáng ngạc nhiên tôi nhận thấy là khi chạy ví dụ này, mã của tôi đã dừng lại sau khi chỉ in In Beginning. Sau đó, khi tôi kích hoạt Lambda của mình một lần nữa, nó đã tiếp tục thực hiện chính xác từ nơi chúng tôi rời đi - và in ra Sau khi thực hiện lần đầu tiên! (Tôi đã thêm chế độ ngủ vì đôi khi Lambda của tôi đã cố gắng kéo một bản in ra trước khi máy cắt tạm dừng nó). Điều này xảy ra lặp đi lặp lại cho đến khi cuộc hành quyết Lambda kết thúc.

Nhật ký Cloudwatch cho việc thực hiện Lambda. Lưu ý rằng chúng tôi có một số id yêu cầu cho cùng Lambda!

Vì vậy, điều này làm cho nó trở nên rõ ràng đối với tôi - máy thái hóa đơn cho chúng tôi miễn là Lambda của chúng tôi có thời gian CPU. Điều đó có nghĩa là thời hạn thanh toán của chúng tôi được tạo thành hai phần:

  1. Thời gian khởi tạo mô-đun (chỉ trong lần gọi đầu tiên / khởi động nguội)
  2. Thời gian thực tế chức năng của chúng tôi

Tránh hết giờ Lambda

Bên cạnh đó là rất tuyệt vời, khám phá này có một thực tế (cũng thực tế là trong mắt của kẻ si tình, nhưng nó chắc chắn là thú vị) sử dụng: xử lý thời gian chờ Lambda! Hãy xem xét Lambda sau đây:

Tôi đã kích hoạt Lambda một lần và nó dừng lại ở dòng 13. Sau đó tôi đợi một lúc và kích hoạt lại nó. Kết quả là thời gian còn lại mà phương thức đối tượng bối cảnh trả về là 0, nhưng Lambda không hết thời gian! Thời gian chờ của Lambda đã được đặt lại vì đây là một yêu cầu khác và hiện tại chúng tôi đã tăng gấp đôi thời gian chờ Lambda của chúng tôi (và tất nhiên là hóa đơn AWS của chúng tôi)! Một trường hợp hữu ích cho việc này, ví dụ, có thể là một vòng lặp xử lý nhiều bản ghi và đôi khi hết thời gian. Bây giờ chúng ta có thể kiểm tra xem chúng ta có sắp hết thời gian không, và nếu vậy hãy gọi lambda_r Yoon.report_done () và chờ kích hoạt tiếp theo để nhận thực thi từ chính xác nơi chúng ta đã tạm dừng!

Nhật ký Cloudwatch từ lời mời Lambda. Thời gian còn lại: 0

Một điều khác xảy ra với tôi khi giải quyết vấn đề là AWS có thể cung cấp một tính năng thực sự dựa trên hành vi này, nơi người dùng có thể tạm ngưng Lambda của mình và tiếp tục từ cùng địa điểm đó trong lần gọi tiếp theo. Điều này có thể hữu ích không chỉ để xử lý lượng dữ liệu đáng kể và xử lý thời gian chờ ở giữa. Một trường hợp sử dụng khác có thể là, ví dụ, đình chỉ Lambda của bạn trong khi chờ đợi IO / một số kết quả nhiệm vụ khác đắt tiền, thay vì trả tiền cho thời gian nhàn rỗi Lambda của bạn! Họ sẽ làm điều đó? Don Tiết biết. Điều đó có tuyệt không? Defo.

Có một nhược điểm của tất cả những điều này, mặc dù. Vì đây là một cách hacky, hai lần gọi tiếp theo của Lambda sẽ thất bại với lỗi nội bộ của Amazon. Tôi chắc chắn một người có thể giải quyết vấn đề này cũng như với một chút nỗ lực, nhưng bây giờ, điều này là đủ tốt cho tôi.

Phần kết luận

Chúng tôi đã học được nhiều về nội bộ AWS Lambda. Chúng tôi đã khám phá các thư viện nhị phân trong môi trường thời gian chạy và hệ thống thanh toán Lambda. Chúng tôi cũng đã nhập thư viện thời gian chạy Lambda và sử dụng nó để xử lý thời gian chờ! Tuy nhiên, vẫn còn nhiều điều cần khám phá, trên AWS và các nhà cung cấp khác. Mong chờ những thử thách tiếp theo, nếu bạn có bất kỳ yêu cầu nào - hãy cho tôi biết!

Tôi cũng đã cập nhật thư viện mã nguồn mở chứa các thử nghiệm khác nhau mà tôi đã tiến hành, Hy vọng bạn sẽ thấy nó hữu ích!

Tại Epsagon, chúng tôi phát triển một công cụ giám sát được thiết kế riêng cho các ứng dụng không có máy chủ. Sử dụng serverless và quan tâm đến việc nghe nhiều hơn? Truy cập trang web của chúng tôi!