Ачааллж байна...
Async.
Тэр үг нь орчин үеийн Python-ы хамгийн их яригддаг, хамгийн их айдас төрүүлдэг сэдвүүдийн нэг юм.
Хэрэв та FastAPI-гийн documentation-ыг нээвэл — async def гэсэн бичлэг хаа сайгүй байна. Форум дээр хүмүүс "async -ыг мэдэхгүй бол FastAPI ашиглаж болохгүй" гэж бичдэг.
Би танд ЭХНЭЭС НЬ шударгаар хэлье:
FastAPI ашиглахын тулд async -ыг ЭЗЭМШИХ ШААРДЛАГАГҮЙ.
FastAPI нь ердийн
def-тэй БҮРЭН ажилладаг.
Тэр нь худал биш, хялбарчлал ч биш. Тэр бол FastAPI-гийн албан ёсны documentation -д бичигдсэн үнэн.
Тэгвэл яагаад async -ыг үзэх ёстой вэ?
Гурван шалтгаанаар:
1. Та түүнийг УНШИХ болно. Documentation, жишээ код, бусдын төслүүд — бүгд async def -тэй байна. Хэрэв та түүнийг харахад айдаг бол — уншиж чадахгүй.
2. Ойлголт нь ХЭРЭГТЭЙ. "Яагаад async байдаг вэ?" гэсэн асуултын хариулт нь компьютер яаж ажилладгийг ойлгуулна.
3. Хэзээ нэгэн цагт хэрэгтэй болно. Хэрэв та олон API -г зэрэг дуудах хэрэгтэй болбол — async нь тэр асуудлыг шийднэ.
Тиймээс Бүлэг 14 нь ГУРВАН БОГИНО хичээл — зөвхөн ОЙЛГОЛТЫН түвшинд.
Энэ хичээлд ганц ч мөр async код байхгүй. Зөвхөн ойлголт. Дараагийн хичээлд бид кодыг УНШИНА. Гурав дахь хичээлд "хэзээ хэрэгтэй вэ" гэсэн шударга хариулт өгнө.
Бүх зүйл нэг үгээс эхэлдэг: ХҮЛЭЭЛТ.
Бүлэг 11.3-ын кодыг санаж байна уу?
python
hariult = requests.get(url, params=parametruud, timeout=10)Тэр мөр ажиллахад юу болдог вэ?
Таны программ хүсэлт илгээнэ
Хүсэлт нь интернэтээр аялна (магадгүй өөр тивд)
Сервер түүнийг боловсруулна
Хариу нь буцаж аялна
Таны программ хариуг хүлээж авна
Тэр бүхэн ойролцоогоор 0.3-1 секунд болно.
Асуулт: тэр 0.5 секундын турш таны программ ЮУ ХИЙЖ БАЙНА ВЭ?
Хариулт: ЮУ Ч ҮГҮЙ.
Тэр зүгээр л ЗОГСОЖ, хүлээж байна. Процессор нь сул зогсож байна. Ямар ч ажил хийгдэхгүй.
Тэр нь БҮГД ХЭВИЙН — хэрэв та ганцхан хүсэлт илгээж байгаа бол.
Гэвч ТАВ бол?
Бүлэг 11.3-ын кодыг санаарай:
python
import requests
import time
khotууd = [
(47.92, 106.92, "Улаанбаатар"),
(49.49, 105.92, "Дархан"),
(49.03, 104.08, "Эрдэнэт"),
(35.68, 139.69, "Токио"),
(51.51, -0.13, "Лондон"),
]
ekhelsen = time.time()
for lat, lon, ner in khotууd:
hariult = requests.get(
"https://api.open-meteo.com/v1/forecast",
params={
"latitude": lat,
"longitude": lon,
"current": "temperature_2m",
"timezone": "auto",
},
timeout=10,
)
temp = hariult.json()["current"]["temperature_2m"]
print(f"{ner:<15} {temp:>6.1f}°C")
duussan = time.time()
print(f"\nНийт хугацаа: {duussan - ekhelsen:.2f} секунд")Улаанбаатар 18.7°C
Дархан 17.2°C
Эрдэнэт 16.9°C
Токио 28.4°C
Лондон 21.3°C
Нийт хугацаа: 2.47 секунд2.47 секунд. Таван хүсэлт, тус бүр нь ойролцоогоор 0.5 секунд.
Гэвч анзаараарай: тэдгээр нь ДАРААЛЛААР явсан.
Улаанбаатар: илгээв → хүлээв → хариу авав (0.5с)
Дархан: илгээв → хүлээв → хариу авав (0.5с)
Эрдэнэт: илгээв → хүлээв → хариу авав (0.5с)
Токио: илгээв → хүлээв → хариу авав (0.5с)
Лондон: илгээв → хүлээв → хариу авав (0.5с)
НИЙТ: 2.5сХамгийн чухал ажиглалт:
Улаанбаатарын хариуг хүлээж байх ТЭР 0.5 секундэд — Дарханы хүсэлтийг илгээх БОЛОМЖТОЙ байсан!
Гэвч бид тэгээгүй. Бид зүгээр л хүлээсэн.
Тэр бол SYNCHRONOUS (синхрон) арга. Нэг ажил дуусахыг хүлээж, дараа нь дараагийнхыг эхлүүлнэ.
Бүлэг 11-ийн ресторан руу буцъя.
МУУ зөөгч (синхрон):
Нэг дэх ширээнд очно
Захиалга авна
Гал тогоо руу очно
ХООЛ БЭЛЭН БОЛТОЛ ТЭНД ЗОГСОНО — 15 минут
Хоолыг авчирна
Дараа нь л хоёр дахь ширээ рүү очно
Таван ширээ = 75 минут.
Зөөгч нь тэр 15 минутын турш ЮУ Ч ХИЙХГҮЙ. Тэр зүгээр л зогсож, хүлээж байна.
Хэрэв та ресторанд ийм зөөгчтэй тааралдвал — та ууртай гарах болно.
САЙН зөөгч (асинхрон):
Нэг дэх ширээнд очно, захиалга авна
Гал тогоонд өгнө
ХҮЛЭЭХГҮЙ — шууд хоёр дахь ширээ рүү явна
Захиалга авна, гал тогоонд өгнө
Гурав дахь, дөрөв дэх, тав дахь ширээ — бүгд
Хоол бэлэн болох бүрд — тэр ширээнд аваачина
Таван ширээ = 20 минут.
Зөөгч НЭГ л хүн байна. Тэр нь илүү хурдан гүйж байгаа юм биш. Тэр нь ХҮЛЭЭЛТИЙН ХУГАЦААНД ӨӨР АЖИЛ ХИЙЖ БАЙНА.
Тэр бол ASYNCHRONOUS (асинхрон) арга.
Одоо хамгийн чухал зүйлийг тодруулъя.
Async нь ажлыг ХУРДАН болгодоггүй.
Async нь ХҮЛЭЭЛТИЙГ ашигтай болгодог.
Гал тогоо нь илүү хурдан болоогүй. Хоол одоо ч 15 минут болно.
Зөвхөн зөөгч нь хүлээхээ болив.
Программын ертөнцөд:
Сервер нь илүү хурдан хариулахгүй
Интернэт нь илүү хурдан болохгүй
Гэвч таны программ хүлээж байхдаа ӨӨР ажил хийж чадна
Одоо чухал ялгааг гаргая. Бүх ажил async -ээс ашиг олохгүй.
Ажил ХОЁР төрөлтэй:
1. I/O-BOUND — "ХҮЛЭЭЛТЭД ХОРИГДСОН"
Программ нь ГАДНЫ зүйлийг хүлээж байна:
Интернэтээс хариу (API, вэб)
Дискнээс файл унших/бичих
Өгөгдлийн сангийн хариу
Хэрэглэгчийн оролт
Процессор нь СУЛ зогсож байна. Тэр нь юу ч тооцоолж байгаа юм биш.
I/O гэдэг нь Input/Output — оролт/гаралт.
2. CPU-BOUND — "ТООЦООЛОЛД ХОРИГДСОН"
Программ нь ЖИНХЭНЭ АЖИЛ хийж байна:
Том тооцоолол
Зураг боловсруулах
Өгөгдөл шинжлэх
Шифрлэлт
Процессор нь 100% ажиллаж байна. Тэр нь хүлээж байгаа юм биш.
Одоо гол дүрэм:
Async нь ЗӨВХӨН I/O-BOUND ажилд тусалдаг.
CPU-BOUND ажилд async нь ЮУ Ч тусалдаггүй.
Яагаад?
Зөөгчийн зүйрлэлээр: зөөгч нь ХҮЛЭЭЖ байхдаа өөр ажил хийж чадна. Гэвч хэрэв тэр ӨӨРӨӨ хоол хийж байвал — тэр нь зэрэг хоёр хоол хийж чадахгүй. Гар нь хоёрхон.
Процессор ч ижил. Хэрэв тэр тооцоолж байвал — тэр нь сул зогсоогүй. Нэмэлт ажил хийх боломж БАЙХГҮЙ.
Жишээ:
python
# I/O-BOUND — async ТУСАЛНА
hariult = requests.get(url) # хүлээж байна
uguudul = json.load(f) # диск хүлээж байна
# CPU-BOUND — async ТУСЛАХГҮЙ
niilber = sum(i * i for i in range(10_000_000)) # тооцоолж байнаЭнэ ялгааг сайн санаж яваарай. Тэр нь Хичээл 14.3-ын гол сэдэв болно.
Одоо хамгийн түгээмэл эндүүрлийг арилгая.
Олон хүн async -ыг "олон зүйл ЗЭРЭГ хийх" гэж боддог.
Тэр нь БУРУУ.
Зөөгч НЭГ л хүн байна. Тэр нь өөрийгөө хувааж чадахгүй. Тэр нь нэг агшинд НЭГ л зүйл хийж байна.
Тэр зөвхөн ХУРДАН СОЛИГДОЖ байна:
Зөөгч: ширээ 1 → гал тогоо → ширээ 2 → гал тогоо → ширээ 3 → ...Тэр нь ХҮЛЭЭЛТИЙН ЦООРХОЙГ ашиглаж байна.
Программын ертөнцөд ижил зүйл:
Async нь НЭГ THREAD дээр ажилладаг. Нэг "ажилчин."
Тэр нь:
Хүсэлт илгээнэ
Хариу хүлээж байхад ӨӨР ажил руу шилжинэ
Хариу ирэхэд — буцаж ирнэ
Тэр нь ХУРДАН СОЛИГДОЛТ, ЗЭРЭГ АЖИЛЛАЛТ БИШ.
Хэрэв танд ЖИНХЭНЭ зэрэг ажиллалт хэрэгтэй бол (олон процессор ашиглах) — тэр нь multiprocessing гэдэг өөр зүйл. Энэ курс түүнийг үзэхгүй.
Ялгааг ТОД харуулъя.
СИНХРОН (5 хүсэлт):
Цаг → 0.0 0.5 1.0 1.5 2.0 2.5
|------|------|------|------|------|
УБ: [ХҮЛЭЭЛТ]
Дархан: [ХҮЛЭЭЛТ]
Эрдэнэт: [ХҮЛЭЭЛТ]
Токио: [ХҮЛЭЭЛТ]
Лондон: [ХҮЛЭЭЛТ]
НИЙТ: 2.5 секундХүлээлтүүд нь ДАРААЛЛААР.
АСИНХРОН (5 хүсэлт):
Цаг → 0.0 0.5
|------|
УБ: [ХҮЛЭЭЛТ]
Дархан:[ХҮЛЭЭЛТ]
Эрдэнэт:[ХҮЛЭЭЛТ]
Токио: [ХҮЛЭЭЛТ]
Лондон:[ХҮЛЭЭЛТ]
НИЙТ: 0.5 секундХүлээлтүүд нь ДАВХЦАЖ байна.
2.5 секунд → 0.5 секунд. ТАВ ДАХИН хурдан.
Гэвч анзаараарай: ХАМГИЙН УДААН хүсэлт нь одоо ч 0.5 секунд болно.
Async нь нэг хүсэлтийг хурдан болгодоггүй. Тэр нь тэднийг ДАВХЦУУЛДАГ.
Одоо async яагаад СЕРВЕР дээр ялангуяа чухал вэ гэдгийг харъя.
Таны FastAPI сервер ажиллаж байна (Бүлэг 15). Нэг хэрэглэгч хүсэлт илгээв.
Синхрон сервер:
Хэрэглэгч А-гийн хүсэлт ирэв
Сервер нь өгөгдлийн сангаас өгөгдөл авахаар хүсэлт илгээв
Сервер ХҮЛЭЭЖ байна (0.1 секунд)
Тэр хугацаанд Хэрэглэгч Б-гийн хүсэлт ирэв — гэвч сервер завгүй
Б нь ХҮЛЭЭХ ЁСТОЙ
Хэрэв 100 хэрэглэгч зэрэг ирвэл — сүүлчийнх нь 10 секунд хүлээнэ.
Асинхрон сервер:
Хэрэглэгч А-гийн хүсэлт ирэв
Сервер нь өгөгдлийн сан руу хүсэлт илгээв
Хүлээхийн оронд — Б-гийн хүсэлтийг эхлүүлэв
Дараа нь В, Г, Д...
Хариу ирэх бүрд — тэр хэрэглэгчид хариулна
100 хэрэглэгч зэрэг — бүгд бараг тэр дор нь.
Тиймээс орчин үеийн вэб framework -үүд бүгд async -ыг дэмждэг. Тэдгээр нь ОЛОН хэрэглэгчийг зэрэг үйлчлэх ёстой.
Одоо би дахин шударга байя.
FastAPI нь ердийн def -тэй ч ЗӨВ ажилладаг.
Хэрэв та ингэж бичвэл:
python
@app.get("/nomnuud")
def nomnuud_jagsaakh(): # ердийн def
return nomnuudFastAPI нь тэр функцийг ТУСДАА ажилчинд (thread) шилжүүлж, бусад хүсэлтийг блоклохгүй.
Тэгэхээр та async БИЧИХГҮЙГЭЭР ч олон хэрэглэгчийг үйлчилж чадна.
Тэгвэл async def хэзээ хэрэгтэй вэ?
Тэр нь Хичээл 14.3-ын сэдэв. Одоохондоо мэдэж байгаарай: сонголт байгаа, албадлага биш.
Эцэст нь нэг чухал зүйл: async нь ҮНЭГҮЙ биш.
Үнэ төлбөр 1: код нь илүү ТӨВӨГТЭЙ.
async def, await, event loop, asyncio.run() — эдгээр бүгдийг мэдэх хэрэгтэй.
Үнэ төлбөр 2: БҮХ ЗАМД async байх ёстой.
Хэрэв та async def бичвэл — түүнийг дуудаж байгаа функц ч бас async def байх ёстой. Тэр нь дээшээ, дээшээ тархана.
Программистууд түүнийг "function coloring problem" (функцийн өнгө) гэж нэрлэдэг. Async функцууд нь "өөр өнгөтэй" бөгөөд тэдгээрийг ердийн функцтэй холихоос болгоомжлох хэрэгтэй.
Үнэ төлбөр 3: сангууд async -ыг дэмжих ёстой.
requests нь async -ыг ДЭМЖДЭГГҮЙ!
Хэрэв та async код бичээд requests.get() дуудвал — тэр нь БҮГДИЙГ БЛОКЛОНО. Async нь утгагүй болно.
Async -тэй ажиллахын тулд httpx эсвэл aiohttp гэсэн ӨӨР сан хэрэгтэй.
Тиймээс: async нь БҮХ ЭКОСИСТЕМИЙГ өөрчилдөг.
Хэрэв сонирхвол: дээрх таван хотын синхрон кодыг ажиллуулж, хугацааг өөрийн нүдээр хараарай. Дараа нь ГУРВАН хот, дараа нь АРВАН хот туршаад — хугацаа шугаман өсөж байгааг мэдрээрэй.
Тэр туршилт нь асуудлыг БОДИТ болгоно. Дараагийн хичээлд бид ижил ажлыг async -ээр хийж, ялгааг харна.
Мөн хүсвэл: CPU-bound ажлыг туршаад үзээрэй:
python
import time
ekhelsen = time.time()
for i in range(5):
niilber = sum(x * x for x in range(5_000_000))
duussan = time.time()
print(f"Хугацаа: {duussan - ekhelsen:.2f} секунд")Энэ ажилд async нь ЮУ Ч тусалахгүй — учир нь тэнд хүлээлт байхгүй, зөвхөн тооцоолол.
1. "Async нь кодыг хурдан болгодог"
БУРУУ. Async нь ХҮЛЭЭЛТИЙГ ашигтай болгодог. Хэрэв хүлээлт байхгүй бол — ямар ч ашиг байхгүй.
2. "Async нь олон зүйлийг ЗЭРЭГ хийдэг"
БУРУУ. Async нь НЭГ ажилчин дээр, ХУРДАН СОЛИГДОЖ ажилладаг. Жинхэнэ зэрэг ажиллалт нь multiprocessing.
3. "Async нь бүх ажилд тусалдаг"
БУРУУ. Зөвхөн I/O-bound (хүлээлттэй) ажилд. CPU-bound (тооцоолол) -д ЮУ Ч тусалахгүй.
4. "FastAPI ашиглахын тулд async мэдэх ЁСТОЙ"
БУРУУ. FastAPI нь ердийн def -тэй бүрэн ажилладаг.
5. "Async нь үнэгүй"
БУРУУ. Код нь илүү төвөгтэй, бүх замд async байх ёстой, сангууд дэмжих ёстой (requests дэмждэггүй!).
Асуудал: ХҮЛЭЭЛТ. requests.get() нь 0.5 секунд ХҮЛЭЭДЭГ. Тэр хугацаанд процессор СУЛ зогсоно.
СИНХРОН — нэг ажил дуусахыг хүлээж, дараа нь дараагийнх. Таван хүсэлт = 2.5 секунд.
АСИНХРОН — хүлээж байхдаа ӨӨР ажил эхлүүлнэ. Таван хүсэлт = 0.5 секунд.
Зүйрлэл: ЗӨӨГЧ. Муу зөөгч нь гал тогоонд зогсож хүлээнэ. Сайн зөөгч нь хүлээхгүй — бусад ширээнд үйлчилнэ.
ГОЛ ОЙЛГОЛТ:
Async нь ажлыг ХУРДАН болгодоггүй. Async нь ХҮЛЭЭЛТИЙГ ашигтай болгодог.
Ажил ХОЁР төрөлтэй:
I/O-BOUND — гадны зүйлийг ХҮЛЭЭЖ байна (интернэт, диск, өгөгдлийн сан). Процессор СУЛ. → async ТУСАЛНА
CPU-BOUND — ЖИНХЭНЭ тооцоолол хийж байна. Процессор 100%. → async ЮУ Ч ТУСЛАХГҮЙ
Async нь ОЛОН ХҮН БИШ. Зөөгч НЭГ л хүн. Тэр зөвхөн ХУРДАН СОЛИГДОЖ байна. Жинхэнэ зэрэг ажиллалт = multiprocessing (энэ курс үзэхгүй).
Серверт ялангуяа чухал: 100 хэрэглэгч зэрэг ирвэл — синхрон сервер нь тэднийг дараалалд оруулна, асинхрон сервер нь бүгдийг зэрэг үйлчилнэ.
ГЭВЧ: FastAPI нь ердийн def -тэй БҮРЭН ажилладаг. Тэр нь функцийг тусдаа ажилчинд шилжүүлдэг.
Async -ийн ҮНЭ ТӨЛБӨР: код төвөгтэй, БҮХ ЗАМД async байх ёстой ("function coloring"), сангууд дэмжих ёстой — requests нь async -ыг ДЭМЖДЭГГҮЙ (httpx, aiohttp хэрэгтэй).
Дараагийн хичээлд бид async кодыг УНШИНА.
async def, await, asyncio.run() — эдгээрийг мөр мөрөөр нь тайлбарлана. Мөн бид дээрх таван хотын жишээг async -ээр бичиж, 2.5 секунд → 0.5 секунд болохыг өөрийн нүдээр харна.
Зорилго нь ЭЗЭМШИХ биш — ТАНИХ. Хичээлийн төгсгөлд та async def гэсэн бичлэг харахад айхаа болих болно.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.