Chức năng không đồng bộ cuộc gọi Python

Chi tiết về cú pháp

@app.get['/']
def results[]:
    results = some_library[]
    return results
0 cho các chức năng vận hành đường dẫn và một số thông tin cơ bản về mã không đồng bộ, đồng thời và song song

Đang vội?¶

TL;DR

Nếu bạn đang sử dụng các thư viện của bên thứ ba yêu cầu bạn gọi cho họ bằng

@app.get['/']
def results[]:
    results = some_library[]
    return results
1, chẳng hạn như

results = await some_library[]

Sau đó, khai báo các chức năng vận hành đường dẫn của bạn với

@app.get['/']
def results[]:
    results = some_library[]
    return results
0 như

@app.get['/']
async def read_results[]:
    results = await some_library[]
    return results

Ghi chú

Bạn chỉ có thể sử dụng

@app.get['/']
def results[]:
    results = some_library[]
    return results
1 bên trong các hàm được tạo bằng
@app.get['/']
def results[]:
    results = some_library[]
    return results
0

Nếu bạn đang sử dụng thư viện của bên thứ ba giao tiếp với thứ gì đó [cơ sở dữ liệu, API, hệ thống tệp, v.v. ] và không hỗ trợ sử dụng

@app.get['/']
def results[]:
    results = some_library[]
    return results
1, [đây là trường hợp hiện tại của hầu hết các thư viện cơ sở dữ liệu], sau đó khai báo các hàm thao tác đường dẫn của bạn như bình thường, chỉ với
@app.get['/']
async def read_results[]:
    results = await some_library[]
    return results
4, như

@app.get['/']
def results[]:
    results = some_library[]
    return results

Nếu ứng dụng của bạn [bằng cách nào đó] không phải giao tiếp với bất kỳ thứ gì khác và đợi nó phản hồi, hãy sử dụng

@app.get['/']
def results[]:
    results = some_library[]
    return results
0

Nếu bạn không biết, hãy sử dụng

@app.get['/']
async def read_results[]:
    results = await some_library[]
    return results
4 bình thường

Ghi chú. Bạn có thể kết hợp

@app.get['/']
async def read_results[]:
    results = await some_library[]
    return results
4 và
@app.get['/']
def results[]:
    results = some_library[]
    return results
0 trong các hàm vận hành đường dẫn của mình bao nhiêu tùy ý và xác định từng hàm bằng cách sử dụng tùy chọn tốt nhất cho bạn. FastAPI sẽ làm điều đúng đắn với họ

Dù sao, trong bất kỳ trường hợp nào ở trên, FastAPI vẫn sẽ hoạt động không đồng bộ và cực kỳ nhanh

Nhưng bằng cách làm theo các bước trên, nó sẽ có thể thực hiện một số tối ưu hóa hiệu suất

Chi tiết kỹ thuật¶

Các phiên bản hiện đại của Python có hỗ trợ "mã không đồng bộ" bằng cách sử dụng thứ gọi là "coroutines", với cú pháp

@app.get['/']
async def read_results[]:
    results = await some_library[]
    return results
9 và
@app.get['/']
def results[]:
    results = some_library[]
    return results
1

Hãy xem cụm từ đó theo từng phần trong các phần bên dưới

  • Mã không đồng bộ
  • @app.get['/']
    async def read_results[]:
        results = await some_library[]
        return results
    
    9 và
    @app.get['/']
    def results[]:
        results = some_library[]
        return results
    
    1
  • quân đoàn

Mã không đồng bộ¶

Mã không đồng bộ chỉ có nghĩa là ngôn ngữ 💬 có cách để nói với máy tính / chương trình 🤖 rằng tại một số điểm trong mã, nó 🤖 sẽ phải đợi một thứ khác hoàn thành ở một nơi khác. Hãy nói rằng một cái gì đó khác được gọi là "tệp chậm" 📝

Vì vậy, trong thời gian đó, máy tính có thể đi làm một số công việc khác, trong khi "slow-file" 📝 kết thúc

Sau đó, máy tính / chương trình 🤖 sẽ quay lại bất cứ khi nào nó có cơ hội vì nó đang đợi một lần nữa hoặc bất cứ khi nào nó 🤖 hoàn thành tất cả công việc của nó vào thời điểm đó. Và nó 🤖 sẽ xem liệu có bất kỳ nhiệm vụ nào mà nó đang chờ đợi đã hoàn thành chưa, làm bất cứ điều gì nó phải làm

Tiếp theo, nó 🤖 nhận nhiệm vụ đầu tiên để hoàn thành [giả sử, "tệp chậm" 📝 của chúng ta] và tiếp tục bất cứ điều gì nó phải làm với nó

"Chờ một cái gì đó khác" thường đề cập đến các thao tác I/O tương đối "chậm" [so với tốc độ của bộ xử lý và bộ nhớ RAM], giống như chờ đợi

  • dữ liệu từ máy khách sẽ được gửi qua mạng
  • dữ liệu do chương trình của bạn gửi sẽ được máy khách nhận qua mạng
  • nội dung của một tệp trong đĩa sẽ được hệ thống đọc và cung cấp cho chương trình của bạn
  • nội dung mà chương trình của bạn cung cấp cho hệ thống sẽ được ghi vào đĩa
  • một hoạt động API từ xa
  • một hoạt động cơ sở dữ liệu để kết thúc
  • một truy vấn cơ sở dữ liệu để trả về kết quả
  • vân vân

Vì thời gian thực hiện được sử dụng chủ yếu bằng cách chờ đợi các thao tác I/O, nên họ gọi chúng là các thao tác "I/O bound"

Nó được gọi là "không đồng bộ" vì máy tính/chương trình không cần phải "đồng bộ hóa" với tác vụ chậm, chờ đợi thời điểm chính xác mà tác vụ kết thúc, trong khi không làm gì, để có thể nhận kết quả và tiếp tục công việc

Thay vào đó, do là một hệ thống "không đồng bộ", sau khi hoàn thành, tác vụ có thể xếp hàng đợi một chút [vài micro giây] để máy tính/chương trình hoàn thành bất kỳ công việc gì nó đã làm, sau đó quay lại lấy kết quả và

Đối với "đồng bộ" [trái ngược với "không đồng bộ"] họ cũng thường sử dụng thuật ngữ "tuần tự", bởi vì máy tính/chương trình tuân theo tất cả các bước theo trình tự trước khi chuyển sang một tác vụ khác, ngay cả khi các bước đó liên quan đến việc chờ đợi

Đồng thời và Burgers¶

Ý tưởng về mã không đồng bộ được mô tả ở trên đôi khi còn được gọi là "đồng thời". Nó khác với "song song"

Đồng thời và song song đều liên quan đến "những điều khác nhau ít nhiều xảy ra cùng một lúc"

Nhưng các chi tiết giữa đồng thời và song song là khá khác nhau

Để thấy sự khác biệt, hãy tưởng tượng câu chuyện sau đây về bánh mì kẹp thịt

Burgers đồng thời¶

Bạn đi cùng người ấy để mua đồ ăn nhanh, bạn đứng xếp hàng trong khi nhân viên thu ngân nhận đơn đặt hàng từ những người trước mặt bạn. 😍

Sau đó đến lượt bạn, bạn đặt 2 chiếc bánh mì kẹp thịt rất lạ mắt cho người bạn yêu và bạn. 🍔🍔

Nhân viên thu ngân nói điều gì đó với đầu bếp trong bếp để họ biết rằng họ phải chuẩn bị bánh mì kẹp thịt cho bạn [mặc dù họ hiện đang chuẩn bị bánh mì kẹp thịt cho những khách hàng trước đó]

Bạn trả. 💸

Nhân viên thu ngân cung cấp cho bạn số lượt của bạn

Trong khi chờ đợi, bạn cùng người ấy đi chọn bàn, bạn ngồi nói chuyện rất lâu với người ấy [vì bánh mì kẹp thịt của bạn rất cầu kỳ và mất nhiều thời gian để chuẩn bị]

Khi bạn đang ngồi cùng bàn với người ấy, trong khi đợi bánh mì kẹp thịt, bạn có thể dành thời gian đó để chiêm ngưỡng người ấy tuyệt vời, dễ thương và thông minh như thế nào ✨😍✨

Trong lúc chờ đợi và nói chuyện với crush, thỉnh thoảng bạn kiểm tra số hiển thị trên quầy xem đã đến lượt mình chưa

Rồi đến một lúc nào đó, cuối cùng cũng đến lượt bạn. Bạn đi đến quầy, lấy bánh mì kẹp thịt của bạn và trở lại bàn

Bạn và người bạn thích ăn bánh mì kẹp thịt và có một khoảng thời gian vui vẻ. ✨

Thông tin

Hình minh họa tuyệt đẹp của Ketrina Thompson. 🎨

Hãy tưởng tượng bạn là máy tính / chương trình 🤖 trong câu chuyện đó

Trong lúc xếp hàng, bạn chỉ nhàn rỗi 😴, đợi đến lượt mình chứ không phải làm việc gì “năng suất” cho lắm. Nhưng xếp hàng nhanh vì nhân viên thu ngân chỉ nhận đơn đặt hàng [không chuẩn bị], nên điều đó ổn

Sau đó, khi đến lượt của bạn, bạn làm công việc "hiệu quả" thực sự, bạn xử lý thực đơn, quyết định những gì bạn muốn, nhận được sự lựa chọn của người ấy, thanh toán, kiểm tra xem bạn có đưa đúng hóa đơn hoặc thẻ hay không, kiểm tra xem bạn có bị tính tiền đúng không, kiểm tra

Nhưng sau đó, mặc dù bạn vẫn chưa có bánh mì kẹp thịt, nhưng công việc của bạn với nhân viên thu ngân đang "tạm dừng" ⏸, bởi vì bạn phải đợi 🕙 để bánh mì kẹp thịt của mình sẵn sàng

Nhưng khi bạn ra khỏi quầy và ngồi vào bàn có đánh số cho lượt của mình, bạn có thể chuyển 🔀 sự chú ý của mình sang người ấy và "làm việc" ⏯ 🤓 trên đó. Sau đó, bạn lại làm một điều gì đó rất "hiệu quả" như là tán tỉnh người bạn thích 😍

Sau đó, nhân viên thu ngân 💁 nói "Tôi làm bánh mì kẹp thịt xong rồi" bằng cách ghi số của bạn lên màn hình của quầy, nhưng bạn không nhảy như điên ngay lập tức khi số hiển thị chuyển sang số lượt của bạn. Bạn biết sẽ không ai ăn cắp bánh mì kẹp thịt của bạn vì bạn có số lượt của mình và họ có lượt của họ

Vì vậy, bạn đợi người ấy kể xong câu chuyện [hoàn thành công việc hiện tại ⏯ / nhiệm vụ đang được xử lý 🤓], mỉm cười dịu dàng và nói rằng bạn sẽ đi ăn bánh mì kẹp thịt ⏸

Sau đó, bạn đến quầy 🔀, đến nhiệm vụ ban đầu đã hoàn thành ⏯, chọn bánh mì kẹp thịt, nói lời cảm ơn và mang chúng đến bàn. Thế là xong bước/nhiệm vụ tương tác với bộ đếm ⏹. Đến lượt nó, tạo ra một nhiệm vụ mới, "ăn bánh mì kẹp thịt" 🔀 ⏯, nhưng nhiệm vụ "lấy bánh mì kẹp thịt" trước đó đã kết thúc ⏹

Burgers song song¶

Bây giờ hãy tưởng tượng đây không phải là "Bánh mì kẹp thịt đồng thời" mà là "Bánh mì kẹp thịt song song"

Bạn đi với người yêu của bạn để có được thức ăn nhanh song song

Bạn đứng xếp hàng trong khi một số [giả sử 8] nhân viên thu ngân đồng thời là đầu bếp nhận đơn đặt hàng từ những người trước mặt bạn

Mọi người trước bạn đang đợi bánh mì kẹp thịt của họ sẵn sàng trước khi rời quầy vì mỗi người trong số 8 nhân viên thu ngân sẽ đi và chuẩn bị bánh mì kẹp thịt ngay lập tức trước khi nhận đơn đặt hàng tiếp theo

Rồi cuối cùng cũng đến lượt bạn, bạn đặt 2 chiếc bánh mì kẹp thịt rất lạ mắt cho người bạn yêu và bạn.

Bạn trả tiền 💸

Thu ngân đi vào bếp

Bạn đợi, đứng trước quầy 🕙, để không ai khác lấy bánh mì kẹp thịt của bạn trước bạn, vì không có số cho lượt

Vì bạn và người ấy đang bận rộn không cho phép bất kỳ ai vượt lên trước mặt bạn và lấy bánh mì kẹp thịt của bạn bất cứ khi nào họ đến, bạn không thể chú ý đến người ấy. 😞

Đây là công việc "đồng bộ", bạn "đồng bộ" với thu ngân/đầu bếp 👨‍🍳. Bạn phải đợi 🕙 và có mặt đúng thời điểm nhân viên thu ngân/đầu bếp 👨‍🍳 ăn xong bánh mì kẹp thịt và đưa cho bạn, nếu không, người khác có thể lấy mất

Sau đó, nhân viên thu ngân/đầu bếp của bạn 👨‍🍳 cuối cùng cũng quay lại với bánh mì kẹp thịt của bạn, sau một thời gian dài chờ đợi 🕙 ở đó trước quầy

Bạn lấy bánh mì kẹp thịt của mình và đi đến bàn với người bạn thích

Bạn chỉ cần ăn chúng, và bạn đã hoàn thành. ⏹

Không có nhiều cuộc trò chuyện hay tán tỉnh vì phần lớn thời gian dành cho việc chờ đợi 🕙 trước quầy. 😞

Thông tin

Hình minh họa tuyệt đẹp của Ketrina Thompson. 🎨

Trong tình huống bánh mì kẹp thịt song song này, bạn là một máy tính / chương trình 🤖 với hai bộ xử lý [bạn và người bạn thích], cả hai đều chờ đợi 🕙 và dành sự chú ý của họ ⏯ để được "đợi trên quầy" 🕙 trong một thời gian dài

Cửa hàng thức ăn nhanh có 8 nhân viên chế biến [thu ngân/đầu bếp]. Trong khi cửa hàng bánh mì kẹp thịt đồng thời có thể chỉ có 2 [một nhân viên thu ngân và một đầu bếp]

Tuy nhiên, trải nghiệm cuối cùng không phải là tốt nhất. 😞

Đây sẽ là câu chuyện tương đương song song cho bánh mì kẹp thịt. 🍔

Để có một ví dụ "thực tế" hơn về điều này, hãy tưởng tượng một ngân hàng

Tính đến thời điểm hiện tại, hầu hết các ngân hàng đều có nhiều quầy thu ngân 👨‍💼👨‍💼👨‍💼👨‍💼 và line lớn 🕙🕙🕙🕙🕙🕙🕙🕙

Tất cả các nhân viên thu ngân làm tất cả công việc với khách hàng này đến khách hàng khác 👨‍💼⏯

Và bạn phải đợi 🕙 trong hàng rất lâu nếu không sẽ bị mất lượt

Chắc hẳn bạn sẽ không muốn đem lòng mình 😍 cùng đi làm việc vặt ở ngân hàng 🏦

Kết luận về Burger¶

Trong kịch bản "bánh mì kẹp thịt thức ăn nhanh với người bạn thích", vì có rất nhiều thời gian chờ đợi 🕙, nên có một hệ thống đồng thời sẽ hợp lý hơn rất nhiều ⏸🔀⏯

Đây là trường hợp của hầu hết các ứng dụng web

Rất, rất nhiều người dùng, nhưng máy chủ của bạn đang đợi 🕙 kết nối kém của họ để gửi yêu cầu của họ

Và sau đó chờ đợi 🕙 một lần nữa để phản hồi trở lại

Sự "chờ đợi" 🕙 này được đo bằng micro giây, nhưng tóm lại, cuối cùng thì cũng rất nhiều thời gian chờ đợi

Đó là lý do tại sao việc sử dụng mã ⏸🔀⏯ không đồng bộ cho API web rất hợp lý

Loại không đồng bộ này là điều khiến NodeJS trở nên phổ biến [mặc dù NodeJS không song song] và đó là thế mạnh của ngôn ngữ lập trình Go

Và đó là cùng mức hiệu suất mà bạn nhận được với FastAPI

Và vì bạn có thể có tính song song và không đồng bộ cùng một lúc, bạn sẽ có hiệu suất cao hơn hầu hết các khung NodeJS đã thử nghiệm và ngang bằng với Go, một ngôn ngữ được biên dịch gần với C hơn [tất cả là nhờ Starlette]

Đồng thời có tốt hơn song song không?¶

Không. Đó không phải là đạo đức của câu chuyện

Đồng thời khác với song song. Và sẽ tốt hơn trong các tình huống cụ thể đòi hỏi nhiều thời gian chờ đợi. Do đó, nó thường tốt hơn nhiều so với tính song song để phát triển ứng dụng web. Nhưng không phải cho tất cả mọi thứ

Vì vậy, để cân bằng điều đó, hãy tưởng tượng câu chuyện ngắn sau đây

Bạn phải dọn dẹp một ngôi nhà lớn, bẩn thỉu

Vâng, đó là toàn bộ câu chuyện

Không phải chờ đợi 🕙 ở bất cứ đâu, chỉ có rất nhiều việc phải làm, ở nhiều nơi trong nhà

Bạn có thể có các lượt như trong ví dụ về bánh mì kẹp thịt, đầu tiên là phòng khách, sau đó là bếp, nhưng vì bạn không chờ đợi 🕙 bất cứ thứ gì, chỉ dọn dẹp và lau chùi, các lượt sẽ không ảnh hưởng gì

Sẽ mất cùng một khoảng thời gian để hoàn thành dù có hoặc không có lượt [đồng thời] và bạn sẽ hoàn thành cùng một lượng công việc

Nhưng trong trường hợp này, nếu bạn có thể mang theo 8 cựu nhân viên thu ngân/đầu bếp/người dọn dẹp hiện tại và mỗi người trong số họ [kể cả bạn] có thể dọn dẹp một khu vực trong nhà, thì bạn có thể làm tất cả công việc song song,

Trong tình huống này, mỗi người trong số những người dọn dẹp [bao gồm cả bạn] sẽ là một bộ xử lý, thực hiện phần công việc của họ

Và vì phần lớn thời gian thực hiện được thực hiện bởi công việc thực tế [thay vì chờ đợi] và công việc trong máy tính được thực hiện bởi CPU, họ gọi những vấn đề này là "CPU bị ràng buộc"

Các ví dụ phổ biến về hoạt động ràng buộc của CPU là những thứ yêu cầu xử lý toán học phức tạp

Ví dụ

  • Xử lý âm thanh hoặc hình ảnh
  • Tầm nhìn máy tính. một hình ảnh bao gồm hàng triệu pixel, mỗi pixel có 3 giá trị/màu, quá trình xử lý thường yêu cầu tính toán một số thứ trên các pixel đó, tất cả cùng một lúc
  • Học máy. nó thường yêu cầu nhiều phép nhân "ma trận" và "vectơ". Hãy nghĩ về một bảng tính khổng lồ với các con số và nhân tất cả chúng với nhau cùng một lúc
  • Học kĩ càng. đây là một lĩnh vực phụ của Machine Learning, vì vậy, điều tương tự cũng được áp dụng. Chỉ là không có một bảng tính số nào để nhân lên, mà là một tập hợp khổng lồ của chúng và trong nhiều trường hợp, bạn sử dụng một bộ xử lý đặc biệt để xây dựng và/hoặc sử dụng các mô hình đó

Đồng thời + Song song. Web + Học máy¶

Với FastAPI, bạn có thể tận dụng lợi thế của tính đồng thời rất phổ biến để phát triển web [điểm thu hút chính tương tự của NodeJS]

Nhưng bạn cũng có thể khai thác các lợi ích của xử lý song song và đa xử lý [có nhiều quy trình chạy song song] đối với khối lượng công việc bị ràng buộc bởi CPU như trong các hệ thống Machine Learning

Điều đó, cộng với thực tế đơn giản rằng Python là ngôn ngữ chính cho Khoa học dữ liệu, Học máy và đặc biệt là Học sâu, làm cho FastAPI trở thành một kết hợp rất tốt cho các ứng dụng và API web Khoa học dữ liệu / Học máy [trong số nhiều ứng dụng khác]

Để biết cách đạt được sự song song này trong quá trình sản xuất, hãy xem phần về Triển khai

@app.get['/']
async def read_results[]:
    results = await some_library[]
    return results
9 và
@app.get['/']
def results[]:
    results = some_library[]
    return results

Các phiên bản hiện đại của Python có một cách rất trực quan để xác định mã không đồng bộ. Điều này làm cho nó trông giống như mã "tuần tự" bình thường và thực hiện "chờ đợi" cho bạn vào đúng thời điểm

Khi có một thao tác yêu cầu đợi trước khi đưa ra kết quả và có hỗ trợ cho các tính năng Python mới này, bạn có thể viết mã như sau

burgers = await get_burgers[2]

Chìa khóa ở đây là

@app.get['/']
def results[]:
    results = some_library[]
    return results
1. Nó nói với Python rằng nó phải đợi ⏸ để
@app.get['/']
def results[]:
    results = some_library[]
    return results
6 hoàn thành công việc của nó 🕙 trước khi lưu kết quả vào
@app.get['/']
def results[]:
    results = some_library[]
    return results
7. Cùng với đó, Python sẽ biết rằng nó có thể đi và làm việc khác 🔀 ⏯ trong khi chờ đợi [chẳng hạn như nhận được một yêu cầu khác]

Để

@app.get['/']
def results[]:
    results = some_library[]
    return results
1 hoạt động, nó phải nằm trong một chức năng hỗ trợ tính không đồng bộ này. Để làm điều đó, bạn chỉ cần khai báo nó với
@app.get['/']
def results[]:
    results = some_library[]
    return results
0

@app.get['/']
async def read_results[]:
    results = await some_library[]
    return results
2

thay vì

@app.get['/']
async def read_results[]:
    results = await some_library[]
    return results
4

@app.get['/']
async def read_results[]:
    results = await some_library[]
    return results
4

Với

@app.get['/']
def results[]:
    results = some_library[]
    return results
0, Python biết rằng, bên trong hàm đó, nó phải nhận thức được các biểu thức của
@app.get['/']
def results[]:
    results = some_library[]
    return results
1 và nó có thể "tạm dừng" ⏸ việc thực thi hàm đó và thực hiện một việc khác 🔀 trước khi quay lại

Khi bạn muốn gọi một hàm

@app.get['/']
def results[]:
    results = some_library[]
    return results
0, bạn phải "chờ đợi" nó. Vì vậy, điều này sẽ không làm việc

@app.get['/']
async def read_results[]:
    results = await some_library[]
    return results
5

Vì vậy, nếu bạn đang sử dụng một thư viện cho bạn biết rằng bạn có thể gọi nó bằng

@app.get['/']
def results[]:
    results = some_library[]
    return results
1, thì bạn cần tạo các hàm thao tác đường dẫn sử dụng nó với
@app.get['/']
def results[]:
    results = some_library[]
    return results
0, như trong

@app.get['/']
async def read_results[]:
    results = await some_library[]
    return results
6

Chi tiết kỹ thuật khác¶

Bạn có thể nhận thấy rằng

@app.get['/']
def results[]:
    results = some_library[]
    return results
1 chỉ có thể được sử dụng bên trong các hàm được xác định bằng
@app.get['/']
def results[]:
    results = some_library[]
    return results
0

Nhưng đồng thời, các chức năng được xác định bằng

@app.get['/']
def results[]:
    results = some_library[]
    return results
0 phải được "chờ đợi". Vì vậy, các hàm có
@app.get['/']
def results[]:
    results = some_library[]
    return results
0 cũng chỉ có thể được gọi bên trong các hàm được xác định bằng
@app.get['/']
def results[]:
    results = some_library[]
    return results
0

Vậy, về quả trứng và con gà, bạn gọi hàm

@app.get['/']
async def read_results[]:
    results = await some_library[]
    return results
9 đầu tiên như thế nào?

Nếu bạn đang làm việc với FastAPI, bạn không phải lo lắng về điều đó, vì chức năng "đầu tiên" đó sẽ là chức năng vận hành đường dẫn của bạn và FastAPI sẽ biết cách thực hiện đúng

Nhưng nếu bạn muốn sử dụng

@app.get['/']
async def read_results[]:
    results = await some_library[]
    return results
9 /
@app.get['/']
def results[]:
    results = some_library[]
    return results
1 mà không cần FastAPI, bạn cũng có thể làm điều đó

Viết mã không đồng bộ của riêng bạn¶

Starlette [và FastAPI] dựa trên AnyIO, giúp nó tương thích với cả thư viện chuẩn của Python là asyncio và Trio

Đặc biệt, bạn có thể trực tiếp sử dụng AnyIO cho các trường hợp sử dụng đồng thời nâng cao yêu cầu các mẫu nâng cao hơn trong mã của riêng bạn

Và ngay cả khi bạn không sử dụng FastAPI, bạn cũng có thể viết các ứng dụng không đồng bộ của riêng mình với AnyIO để tương thích cao và nhận được các lợi ích của nó [e. g. đồng thời có cấu trúc]

Các dạng mã không đồng bộ khác¶

Phong cách sử dụng

@app.get['/']
async def read_results[]:
    results = await some_library[]
    return results
9 và
@app.get['/']
def results[]:
    results = some_library[]
    return results
1 này tương đối mới trong ngôn ngữ

Nhưng nó giúp làm việc với mã không đồng bộ dễ dàng hơn rất nhiều

Cú pháp tương tự này [hoặc gần như giống hệt] gần đây cũng được đưa vào các phiên bản JavaScript hiện đại [trong Trình duyệt và NodeJS]

Nhưng trước đó, việc xử lý mã không đồng bộ khá phức tạp và khó khăn hơn

Trong các phiên bản trước của Python, bạn có thể đã sử dụng các luồng hoặc Gevent. Nhưng mã phức tạp hơn nhiều để hiểu, gỡ lỗi và suy nghĩ về

Trong các phiên bản trước của NodeJS/Trình duyệt JavaScript, bạn sẽ sử dụng "gọi lại". Điều này dẫn đến địa ngục gọi lại

Coroutines¶

Coroutine chỉ là một thuật ngữ rất hoa mỹ cho thứ được trả về bởi hàm

@app.get['/']
def results[]:
    results = some_library[]
    return results
0. Python biết rằng nó giống như một chức năng mà nó có thể bắt đầu và nó sẽ kết thúc vào một thời điểm nào đó, nhưng nó cũng có thể bị tạm dừng ⏸ bên trong, bất cứ khi nào có một
@app.get['/']
def results[]:
    results = some_library[]
    return results
1 bên trong nó

Nhưng tất cả chức năng sử dụng mã không đồng bộ này với

@app.get['/']
async def read_results[]:
    results = await some_library[]
    return results
9 và
@app.get['/']
def results[]:
    results = some_library[]
    return results
1 nhiều lần được tóm tắt là sử dụng "coroutines". Nó có thể so sánh với tính năng chính của Go, "Goroutines"

Phần kết luận¶

Hãy xem cụm từ tương tự từ trên

Các phiên bản hiện đại của Python có hỗ trợ "mã không đồng bộ" bằng cách sử dụng thứ gọi là "coroutines", với cú pháp

@app.get['/']
async def read_results[]:
    results = await some_library[]
    return results
9 và
@app.get['/']
def results[]:
    results = some_library[]
    return results
1

Điều đó sẽ có ý nghĩa hơn bây giờ. ✨

Tất cả những điều đó là sức mạnh của FastAPI [thông qua Starlette] và điều khiến nó có hiệu suất ấn tượng như vậy

Chi tiết Kỹ thuật¶

Cảnh báo

Bạn có thể có thể bỏ qua điều này

Đây là những chi tiết rất kỹ thuật về cách FastAPI hoạt động bên dưới

Nếu bạn có khá nhiều kiến ​​thức kỹ thuật [đồng quy trình, chủ đề, chặn, v.v. ] và tò mò về cách FastAPI xử lý

@app.get['/']
def results[]:
    results = some_library[]
    return results
0 so với
@app.get['/']
async def read_results[]:
    results = await some_library[]
    return results
4 thông thường, hãy tiếp tục

Hàm vận hành đường dẫn¶

Khi bạn khai báo một hàm vận hành đường dẫn với

@app.get['/']
async def read_results[]:
    results = await some_library[]
    return results
4 bình thường thay vì
@app.get['/']
def results[]:
    results = some_library[]
    return results
0, nó sẽ được chạy trong một nhóm luồng bên ngoài sau đó được chờ, thay vì được gọi trực tiếp [vì nó sẽ chặn máy chủ]

Nếu bạn đến từ một khung công tác không đồng bộ khác không hoạt động theo cách được mô tả ở trên và bạn đã quen với việc xác định các hàm vận hành đường dẫn chỉ dành cho điện toán thông thường với

@app.get['/']
async def read_results[]:
    results = await some_library[]
    return results
4 đơn giản để đạt được hiệu suất nhỏ [khoảng 100 nano giây], xin lưu ý rằng trong FastAPI, . Trong những trường hợp này, tốt hơn là sử dụng
@app.get['/']
def results[]:
    results = some_library[]
    return results
0 trừ khi các chức năng vận hành đường dẫn của bạn sử dụng mã thực hiện chặn I/O

Tuy nhiên, trong cả hai trường hợp, rất có thể FastAPI sẽ vẫn nhanh hơn [hoặc ít nhất là tương đương với] khuôn khổ trước đây của bạn

Phụ thuộc¶

Điều tương tự áp dụng cho các phụ thuộc. Nếu một phần phụ thuộc là một hàm

@app.get['/']
async def read_results[]:
    results = await some_library[]
    return results
4 tiêu chuẩn thay vì
@app.get['/']
def results[]:
    results = some_library[]
    return results
0, thì nó sẽ được chạy trong nhóm luồng bên ngoài

Phụ thuộc¶

Bạn có thể có nhiều phụ thuộc và phụ thuộc yêu cầu lẫn nhau [dưới dạng tham số của định nghĩa hàm], một số trong số chúng có thể được tạo bằng

@app.get['/']
def results[]:
    results = some_library[]
    return results
0 và một số có
@app.get['/']
async def read_results[]:
    results = await some_library[]
    return results
4 bình thường. Nó vẫn hoạt động và những cái được tạo bằng
@app.get['/']
async def read_results[]:
    results = await some_library[]
    return results
4 bình thường sẽ được gọi trên một luồng bên ngoài [từ nhóm luồng] thay vì được "chờ đợi"

Các chức năng tiện ích khác¶

Bất kỳ chức năng tiện ích nào khác mà bạn gọi trực tiếp đều có thể được tạo bằng

@app.get['/']
async def read_results[]:
    results = await some_library[]
    return results
4 hoặc
@app.get['/']
def results[]:
    results = some_library[]
    return results
0 bình thường và FastAPI sẽ không ảnh hưởng đến cách bạn gọi nó

Điều này trái ngược với các chức năng mà FastAPI gọi cho bạn. chức năng hoạt động đường dẫn và phụ thuộc

Nếu chức năng tiện ích của bạn là một chức năng bình thường với

@app.get['/']
async def read_results[]:
    results = await some_library[]
    return results
4, thì nó sẽ được gọi trực tiếp [khi bạn viết nó trong mã của mình], không phải trong nhóm luồng, nếu chức năng được tạo bằng
@app.get['/']
def results[]:
    results = some_library[]
    return results
0 thì bạn nên
@app.get['/']
def results[]:
    results = some_library[]
    return results
1 cho chức năng đó khi bạn gọi nó trong

Một lần nữa, đây là những chi tiết rất kỹ thuật có thể sẽ hữu ích nếu bạn tìm kiếm chúng

Bạn có thể gọi một hàm async mà không cần chờ Python không?

Quy tắc. không-async-không chờ đợi . Functions marked async must contain an await or return statement.

Làm cách nào để sử dụng async đang chờ trong Python?

Từ khóa không đồng bộ/đang chờ . Từ khóa async được sử dụng để tạo một coroutine Python. Từ khóa chờ tạm dừng việc thực thi một quy trình đăng ký cho đến khi quy trình đó hoàn thành và trả về dữ liệu kết quả . Các từ khóa chờ đợi chỉ hoạt động trong chức năng không đồng bộ.

Chủ Đề