از طریق منوی جستجو مطلب مورد نظر خود در وبلاگ را به سرعت پیدا کنید
پایتون ناهمزمان برای توسعه وب
سرفصلهای مطلب
برنامه نویسی ناهمزمان برای کارهایی که شامل خواندن و نوشتن مکرر فایل ها یا ارسال داده ها از سرور به عقب و جلو می باشد، مناسب است. برنامههای ناهمزمان عملیات ورودی/خروجی را به صورت غیرمسدود انجام میدهند، به این معنی که میتوانند وظایف دیگری را در حالی که منتظر بازگشت دادهها از مشتری هستند، انجام دهند، نه اینکه منتظر بمانند و منابع و زمان را هدر دهند.
پایتون، مانند بسیاری از زبان های دیگر، به طور پیش فرض از ناهمزمان نبودن رنج می برد. خوشبختانه، تغییرات سریع در دنیای فناوری اطلاعات به ما این امکان را می دهد که کدهای ناهمزمان را حتی با استفاده از زبان هایی بنویسیم که در ابتدا قرار نبوده اند. در طول سالها، تقاضا برای سرعت از تواناییهای سختافزاری فراتر رفته و شرکتها در سراسر جهان با مانیفست واکنشی به منظور مقابله با این موضوع.
رفتار غیرمسدود کننده برنامه های ناهمزمان می تواند منجر به مزایای عملکرد قابل توجهی در زمینه یک برنامه وب شود و به حل مشکل توسعه برنامه های واکنشی کمک کند.
برخی از ابزارهای قدرتمند برای نوشتن برنامه های ناهمزمان که در پایتون 3 ساخته شده اند. در این مقاله، ما برخی از این ابزارها را پوشش خواهیم داد، به خصوص که مربوط به توسعه وب هستند.
ما یک واکنشگر ساده را توسعه خواهیم داد aiohttp برنامه مبتنی بر نمایش مختصات فعلی آسمان مربوط به سیارات از منظومه شمسی، با توجه به مختصات جغرافیایی کاربر. می توانید برنامه را پیدا کنید اینجاو کد منبع اینجا.
ما در پایان با بحث در مورد چگونگی آماده سازی برنامه برای استقرار به پایان خواهیم رسید هروکو.
مقدمه ای بر پایتون ناهمزمان
برای کسانی که با نوشتن کد سنتی پایتون آشنا هستند، پرش به کد ناهمزمان میتواند از نظر مفهومی کمی دشوار باشد. کد ناهمزمان در پایتون متکی است روی روتین ها، که در ارتباط با یک حلقه رویداد امکان نوشتن کدی را فراهم می کند که به نظر می رسد بیش از یک کار را همزمان انجام می دهد.
کوروتین ها را می توان به عنوان توابعی در نظر گرفت که دارای نقاطی در کد هستند که کنترل برنامه را به زمینه فراخوانی برمی گرداند. این نقاط “بازده” علاوه بر تبادل داده ها بین زمینه ها، امکان توقف و از سرگیری اجرای کوروتین را فراهم می کند.
حلقه رویداد تصمیم میگیرد که چه تکهای از کد در هر لحظه اجرا شود – این حلقه مسئول مکث، از سرگیری و برقراری ارتباط بین کوروتینها است. این بدان معناست که بخشهایی از برنامههای مختلف ممکن است به ترتیبی غیر از ترتیبی که در آن برنامهریزی شده بودند اجرا شوند. این ایده اجرای قطعات مختلف کد بدون نظم نامیده می شود همزمانی.
اندیشیدن به همزمانی در زمینه ساختن HTTP
درخواست ها می توانند روشن شوند. تصور کنید که می خواهید درخواست های مستقل زیادی به یک سرور بدهید. برای مثال، ممکن است بخواهیم از یک وب سایت پرس و جو کنیم تا آماری در مورد همه بازیکنان ورزشی در یک فصل مشخص به دست آوریم.
ما میتوانست هر درخواست را به ترتیب انجام دهید با این حال، با هر درخواست، میتوانیم تصور کنیم که کد خروجی ممکن است مدتی را منتظر بمانند تا درخواستی به سرور تحویل داده شود و پاسخ ارسال شود.
گاهی اوقات، این عملیات ممکن است حتی چندین ثانیه طول بکشد. برنامه ممکن است به دلیل تعداد زیاد کاربران یا صرفاً به دلیل محدودیت سرعت سرور داده شده، تاخیر شبکه را تجربه کند.
چه میشود اگر کد ما بتواند کارهای دیگری را در حین انتظار برای پاسخ از سرور انجام دهد؟ علاوه بر این، اگر تنها پس از رسیدن اطلاعات پاسخ به پردازش یک درخواست معین بازگردد، چه؟ اگر مجبور نباشیم منتظر بمانیم تا هر یک از درخواستها قبل از اینکه به درخواست بعدی در لیست برویم، میتوانیم درخواستهای زیادی را پشت سر هم انجام دهیم.
کوروتینها با یک حلقه رویداد به ما امکان میدهند کدی بنویسیم که دقیقاً به این شکل عمل میکند.
asyncio
asyncio، بخشی از کتابخانه استاندارد پایتون، یک حلقه رویداد و مجموعه ای از ابزارها را برای کنترل آن فراهم می کند. با asyncio ما می توانیم کوروتین ها را برای اجرا برنامه ریزی کنیم، و کوروتین های جدیدی ایجاد کنیم (واقعا asyncio.Task
اشیاء، با استفاده از اصطلاح asyncio) که تنها زمانی اجرا را به پایان میرساند که اجرای کوروتینهای سازنده به پایان برسد.
برخلاف سایر زبانهای برنامهنویسی ناهمزمان، پایتون ما را مجبور نمیکند از حلقه رویدادی که با زبان ارسال میشود استفاده کنیم. مانند برت کانن اشاره می کند، کوروتین های پایتون یک API ناهمزمان را تشکیل می دهند که با آن می توانیم از هر حلقه رویداد استفاده کنیم. پروژه هایی وجود دارند که یک حلقه رویداد کاملاً متفاوت را پیاده سازی می کنند کنجکاوی، یا اجازه حذف در یک سیاست حلقه رویداد دیگر را برای asyncio (سیاست حلقه رویداد چیزی است که حلقه رویداد را “پشت صحنه” مدیریت می کند)، مانند uvloop.
بیایید به یک قطعه کد نگاهی بیندازیم که دو برنامه را به طور همزمان اجرا می کند و هر کدام یک پیام را پس از یک ثانیه چاپ می کند:
import asyncio
async def wait_around(n, name):
for i in range(n):
print(f"{name}: iteration {i}")
await asyncio.sleep(1.0)
async def main():
await asyncio.gather(*(
wait_around(2, "coroutine 0"), wait_around(5, "coroutine 1")
))
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
me@local:~$ time python example1.py
coroutine 1: iteration 0
coroutine 0: iteration 0
coroutine 1: iteration 1
coroutine 0: iteration 1
coroutine 1: iteration 2
coroutine 1: iteration 3
coroutine 1: iteration 4
real 0m5.138s
user 0m0.111s
sys 0m0.019s
این کد تقریباً در 5 ثانیه اجرا می شود asyncio.sleep
coroutine نقاطی را ایجاد می کند که در آن حلقه رویداد می تواند به اجرای کدهای دیگر بپرد. علاوه بر این، به حلقه رویداد گفته ایم که هر دو را برنامه ریزی کند wait_around
نمونه هایی برای اجرای همزمان با asyncio.gather
تابع.
asyncio.gather
فهرستی از “موارد قابل انتظار” (به عنوان مثال، روالها، یا asyncio.Task
اشیاء) و یک واحد را برمی گرداند asyncio.Task
شی ای که فقط زمانی تمام می شود که تمام وظایف/کوروتین های تشکیل دهنده آن به پایان برسد. دو خط آخر هستند asyncio
دیگ بخار برای اجرای یک کوروتین معین تا پایان اجرای آن.
کوروتین ها، بر خلاف توابع، بلافاصله پس از فراخوانی شروع به اجرا نمی کنند. را await
کلمه کلیدی چیزی است که به حلقه رویداد میگوید تا یک کوروتین را برای اجرا برنامهریزی کند.
اگر ما را بیرون بیاوریم await
در مقابل asyncio.sleep
، برنامه بلافاصله (تقریبا) به پایان می رسد، زیرا ما به حلقه رویداد نگفته ایم که در واقع کوروتین را اجرا کند، که در این مورد به کوروتین می گوید که برای مدت زمان مشخصی مکث کند.
با درک اینکه کدهای ناهمزمان پایتون چگونه به نظر می رسد، بیایید حرکت کنیم روی به توسعه وب ناهمزمان
نصب aiohttp
aiohttp یک کتابخانه پایتون برای ایجاد ناهمزمان است HTTP
درخواست ها. علاوه بر این، چارچوبی برای کنار هم قرار دادن بخش سرور یک برنامه وب فراهم می کند. استفاده از پایتون 3.5+ و pip، می توانیم نصب کنیم aiohttp:
pip install --user aiohttp
سمت مشتری: ایجاد درخواست
مثالهای زیر نشان میدهند که چگونه میتوانیم محتوای HTML وبسایت “example.com” را با استفاده از آن دانلود کنیم aiohttp:
import asyncio
import aiohttp
async def make_request():
url = "https://example.com"
print(f"making request to {url}")
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
if resp.status == 200:
print(await resp.text())
loop = asyncio.get_event_loop()
loop.run_until_complete(make_request())
چند نکته قابل تاکید:
- خیلی شبیه با
await asyncio.sleep
ما باید استفاده کنیدawait
باresp.text()
به منظور دریافت محتوای HTML از page. اگر آن را کنار بگذاریم، خروجی برنامه ما چیزی شبیه به زیر خواهد بود:
me@local:~$ python example2_basic_aiohttp_request.py
<coroutine object ClientResponse.text at 0x7fe64e574ba0>
-
async with
یک مدیر زمینه است که به جای توابع با کوروتین ها کار می کند. در هر دو موردی که از آن استفاده می شود، می توانیم تصور کنیم که در داخل، aiohttp بستن اتصالات به سرورها یا آزاد کردن منابع است. -
aiohttp.ClientSession
روش هایی دارد که مطابقت دارند HTTP افعال در همان
راه آنsession.get
در حال ساختن یک گرفتن درخواست،session.post
یک را می سازد پست درخواست.
این مثال به خودی خود هیچ مزیت عملکردی نسبت به درخواست HTTP همزمان ندارد. زیبایی واقعی سمت مشتری aiohttp در ایجاد چندین درخواست همزمان نهفته است:
import asyncio
import aiohttp
async def make_request(session, req_n):
url = "https://example.com"
print(f"making request {req_n} to {url}")
async with session.get(url) as resp:
if resp.status == 200:
await resp.text()
async def main():
n_requests = 100
async with aiohttp.ClientSession() as session:
await asyncio.gather(
*(make_request(session, i) for i in range(n_requests))
)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
به جای اینکه هر درخواست را به صورت متوالی انجام دهیم، درخواست می کنیم asyncio
برای انجام آنها به طور همزمان، با asycio.gather
.
برنامه وب PlanetTracker
در طول این بخش، من قصد دارم نشان دهم که چگونه می توان یک برنامه را جمع آوری کرد که مختصات فعلی سیارات در آسمان را در مکان کاربر (ephemerides) گزارش می کند.
کاربر موقعیت مکانی خود را با وب ارائه می کند API مکان جغرافیایی، که کار را برای ما انجام می دهد.
من در پایان با نشان دادن روش تنظیم a نمایه به منظور استقرار برنامه روی هروکو. اگر برنامه ریزی می کنید روی همانطور که من در کنار هم قرار دادن برنامه کار می کنم، باید موارد زیر را انجام دهید، با فرض اینکه پایتون 3.6 و pip نصب شده است:
me@local:~$ mkdir planettracker && cd planettracker
me@local:~/planettracker$ pip install --user pipenv
me@local:~/planettracker$ pipenv --python=3
Planet Ephemerides با PyEphem
زودگذر یک شی نجومی موقعیت فعلی آن در آسمان در یک مکان و زمان معین است روی زمین. PyEphem یک کتابخانه پایتون است که امکان محاسبه دقیق ephemerides را فراهم می کند.
این به ویژه برای کار در دست مناسب است، زیرا دارای اشیاء نجومی معمولی است که در کتابخانه پخته می شوند. ابتدا بیایید نصب کنیم PyEphem:
me@local:~/planettracker$ pipenv install ephem
بدست آوردن مختصات فعلی مریخ به سادگی استفاده از یک نمونه از مریخ است Observer
کلاس به compute
مختصات آن:
import ephem
import math
convert = math.pi / 180.
mars = ephem.Mars()
greenwich = ephem.Observer()
greenwich.lat = "51.4769"
greenwich.lon = "-0.0005"
mars.compute(observer)
az_deg, alt_deg = mars.az*convert, mars.alt*convert
print(f"Mars' current azimuth and elevation: {az_deg:.2f} {alt_deg:.2f}")
به منظور آسانتر کردن اپیمریدهای سیارهای، بیایید یک کلاس راهاندازی کنیم PlanetTracker
با روشی که ارتفاع و ارتفاع فعلی یک سیاره را بر حسب درجه برمی گرداند (PyEphem به طور پیشفرض از رادیان استفاده میکند، نه درجه، برای نشان دادن زوایای داخلی):
import math
import ephem
class PlanetTracker(ephem.Observer):
def __init__(self):
super(PlanetTracker, self).__init__()
self.planets = {
"mercury": ephem.Mercury(),
"venus": ephem.Venus(),
"mars": ephem.Mars(),
"jupiter": ephem.Jupiter(),
"saturn": ephem.Saturn(),
"uranus": ephem.Uranus(),
"neptune": ephem.Neptune()
}
def calc_planet(self, planet_name, when=None):
convert = 180./math.pi
if when is None:
when = ephem.now()
self.date = when
if planet_name in self.planets:
planet = self.planets(planet_name)
planet.compute(self)
return {
"az": float(planet.az)*convert,
"alt": float(planet.alt)*convert,
"name": planet_name
}
else:
raise KeyError(f"Couldn't find {planet_name} in planets dict")
اکنون می توانیم هر یک از هفت سیاره دیگر در منظومه شمسی را به راحتی بدست آوریم:
from planet_tracker import PlanetTracker
tracker = PlanetTracker()
tracker.lat = "51.4769"
tracker.lon = "-0.0005"
tracker.calc_planet("mars")
اجرای این قطعه کد به دست می آید:
{'az': 92.90019644871396, 'alt': -23.146670983905302, 'name': 'mars'}
سمت سرور aiohttp: مسیرهای HTTP
با توجه به طول و عرض جغرافیایی، ما به راحتی می توانیم زودگذر فعلی یک سیاره را بر حسب درجه بدست آوریم. حالا بیایید یک را راه اندازی کنیم aiohttp مسیری که به مشتری اجازه می دهد تا با توجه به موقعیت جغرافیایی کاربر، گذر زمان سیاره را بدست آورد.
قبل از اینکه بتوانیم کدنویسی را شروع کنیم، باید در مورد چه چیزی فکر کنیم HTTP افعالی که می خواهیم با هر یک از این وظایف مرتبط کنیم. استفاده منطقی است پست برای اولین کار، همانطور که ما هستیم تنظیمات مختصات جغرافیایی ناظر با توجه به اینکه ما هستیم گرفتن ephemerides، استفاده از آن منطقی است گرفتن برای کار دوم:
from aiohttp import web
from planet_tracker import PlanetTracker
@routes.get("/planets/{name}")
async def get_planet_ephmeris(request):
planet_name = request.match_info('name')
data = request.query
try:
geo_location_data = {
"lon": str(data("lon")),
"lat": str(data("lat")),
"elevation": float(data("elevation"))
}
except KeyError as err:
geo_location_data = {
"lon": "-0.0005",
"lat": "51.4769",
"elevation": 0.0,
}
print(f"get_planet_ephmeris: {planet_name}, {geo_location_data}")
tracker = PlanetTracker()
tracker.lon = geo_location_data("lon")
tracker.lat = geo_location_data("lat")
tracker.elevation = geo_location_data("elevation")
planet_data = tracker.calc_planet(planet_name)
return web.json_response(planet_data)
app = web.Application()
app.add_routes(routes)
web.run_app(app, host="localhost", port=8000)
اینجا route.get
دکوراتور نشان می دهد که ما می خواهیم get_planet_ephmeris
coroutine به عنوان کنترل کننده یک متغیر GET
مسیر
قبل از اجرای این، بیایید نصب کنیم aiohttp با pipenv:
me@local:~/planettracker$ pipenv install aiohttp
اکنون می توانیم برنامه خود را اجرا کنیم:
me@local:~/planettracker$ pipenv run python aiohttp_app.py
وقتی این را اجرا می کنیم، می توانیم مرورگر خود را به مسیرهای مختلف خود هدایت کنیم تا داده هایی را که سرور ما برمی گرداند ببینیم. اگر بگذارم localhost:8000/planets/mars
در نوار آدرس مرورگرم، باید پاسخی مانند زیر ببینم:
{"az": 98.72414165963292, "alt": -18.720718647020792, "name": "mars"}
این همان صدور زیر است حلقه دستور:
me@local:~$ curl localhost:8000/planets/mars
{"az": 98.72414165963292, "alt": -18.720718647020792, "name": "mars"}
اگر آشنایی ندارید حلقه، راحت است command-line ابزاری برای آزمایش مسیرهای HTTP شما از جمله موارد دیگر.
ما می توانیم عرضه کنیم گرفتن URL به حلقه:
me@local:~$ curl localhost:8000/planets/mars
{"az": 98.72414165963292, "alt": -18.720718647020792, "name": "mars"}
این به ما گذر زمان مریخ را در رصدخانه گرینویچ در بریتانیا می دهد.
ما می توانیم مختصات را در آدرس URL کدگذاری کنیم GET
درخواست کنید تا بتوانیم گذر زمان مریخ را در مکان های دیگر دریافت کنیم (به نقل قول های اطراف URL توجه کنید):
me@local:~$ curl "localhost:8000/planets/mars؟lon=145.051&lat=-39.754&elevation=0"
{"az": 102.30273048280189, "alt": 11.690380174890928, "name": "mars"
curl
همچنین می تواند برای ایجاد درخواست های POST نیز استفاده شود:
me@local:~$ curl --header "Content-Type: application/x-www-form-urlencoded" --data "lat=48.93&lon=2.45&elevation=0" localhost:8000/geo_location
{"lon": "2.45", "lat": "48.93", "elevation": 0.0}
توجه داشته باشید که با ارائه --data
رشته، curl
به طور خودکار فرض می کند که ما در حال انجام یک درخواست POST هستیم.
قبل از اینکه حرکت کنیم روی، باید توجه داشته باشم که web.run_app
تابع برنامه ما را به صورت مسدود کننده اجرا می کند. قطعاً این چیزی نیست که ما به دنبال آن هستیم!
برای اجرای همزمان، باید کمی کد اضافه کنیم:
import asyncio
...
async def start_app():
runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(
runner, parsed.host, parsed.port)
await site.start()
print(f"Serving up app روی {parsed.host}:{parsed.port}")
return runner, site
loop = asyncio.get_event_loop()
runner, site = loop.run_until_complete(start_async_app())
try:
loop.run_forever()
except KeyboardInterrupt as err:
loop.run_until_complete(runner.cleanup())
به حضور توجه کنید loop.run_forever
به جای تماس به loop.run_until_complete
که قبلا دیدیم بهجای اجرای مجموعهای از کوروتینها، میخواهیم برنامه ما سروری راهاندازی کند که درخواستها را تا زمانی که با ctrl+c
، در این مرحله سرور را به آرامی خاموش می کند.
کلاینت HTML/JavaScript
aiohttp به ما اجازه می دهد تا فایل های HTML و جاوا اسکریپت را ارائه دهیم. استفاده کردن aiohttp برای ارائه دارایی های “ایستا” مانند CSS و جاوا اسکریپت توصیه نمی شود، اما برای اهداف این برنامه، نباید مشکلی ایجاد کند.
بیایید چند خط به خط خود اضافه کنیم aiohttp_app.py
فایل برای ارائه یک فایل HTML که به یک فایل جاوا اسکریپت ارجاع می دهد:
...
@routes.get('/')
async def hello(request):
return web.FileResponse("./index.html")
app = web.Application()
app.add_routes(routes)
app.router.add_static("/", "./")
...
را hello
coroutine راه اندازی یک مسیر GET در localhost:8000/
که در خدمت مطالب index.html
، در همان دایرکتوری قرار دارد که سرور خود را از آن اجرا می کنیم.
را app.router.add_static
خط در حال راه اندازی یک مسیر در localhost:8000/
برای ارائه فایلها در همان دایرکتوری که سرور خود را از آن اجرا میکنیم. این بدان معنی است که مرورگر ما قادر خواهد بود فایل جاوا اسکریپت مورد نظر ما را پیدا کند index.html
.
توجه داشته باشید: در تولید، انتقال فایلهای HTML، CSS و JS به یک دایرکتوری جداگانه که سرویس میشود منطقی است. روی خودش این باعث می شود که کاربر کنجکاو نتواند به کد سرور ما دسترسی پیدا کند.
فایل HTML بسیار ساده است:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Planet Tracker</title>
</head>
<body>
<div id="app">
<label id="lon">Longitude: <input type="text"/></label><br/>
<label id="lat">Latitude: <input type="text"/></label><br/>
<label id="elevation">Elevation: <input type="text"/></label><br/>
</div>
<script src="/app.js"></script>
</body>
اگرچه، فایل جاوا اسکریپت کمی بیشتر درگیر است:
var App = function() {
this.planetNames = (
"mercury",
"venus",
"mars",
"jupiter",
"saturn",
"uranus",
"neptune"
)
this.geoLocationIds = (
"lon",
"lat",
"elevation"
)
this.keyUpInterval = 500
this.keyUpTimer = null
this.planetDisplayCreated = false
this.updateInterval = 2000
this.updateTimer = null
this.geoLocation = null
this.init = function() {
this.getGeoLocation().then((position) => {
var coords = this.processCoordinates(position)
this.geoLocation = coords
this.initGeoLocationDisplay()
this.updateGeoLocationDisplay()
return this.getPlanetEphemerides()
}).then((planetData) => {
this.createPlanetDisplay()
this.updatePlanetDisplay(planetData)
}).then(() => {
return this.initUpdateTimer()
})
}
this.update = function() {
if (this.planetDisplayCreated) {
this.getPlanetEphemerides().then((planetData) => {
this.updatePlanetDisplay(planetData)
})
}
}
this.get = function(url, data) {
var request = new XMLHttpRequest()
if (data !== undefined) {
url += `?${data}`
}
request.open("GET", url, true)
return new Promise((resolve, reject) => {
request.send()
request.onreadystatechange = function(){
if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
resolve(this)
}
}
request.onerror = reject
})
}
this.processCoordinates = function(position) {
var coordMap = {
'longitude': 'lon',
'latitude': 'lat',
'altitude': 'elevation'
}
var coords = Object.keys(coordMap).reduce((obj, name) => {
var coord = position.coords(name)
if (coord === null || isNaN(coord)) {
coord = 0.0
}
obj(coordMap(name)) = coord
return obj
}, {})
return coords
}
this.coordDataUrl = function (coords) {
postUrl = Object.keys(coords).map((c) => {
return `${c}=${coords(c)}`
})
return postUrl
}
this.getGeoLocation = function() {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve)
})
}
this.getPlanetEphemeris = function(planetName) {
var postUrlArr = this.coordDataUrl(this.geoLocation)
return this.get(`/planets/${planetName}`, postUrlArr.join("&")).then((req) => {
return JSON.parse(req.response)
})
}
this.getPlanetEphemerides = function() {
return Promise.all(
this.planetNames.map((name) => {
return this.getPlanetEphemeris(name)
})
)
}
this.createPlanetDisplay = function() {
var div = document.getElementById("app")
var table = document.createElement("table")
var header = document.createElement("tr")
var headerNames = ("Name", "Azimuth", "Altitude")
headerNames.forEach((headerName) => {
var headerElement = document.createElement("th")
headerElement.textContent = headerName
header.appendChild(headerElement)
})
table.appendChild(header)
this.planetNames.forEach((name) => {
var planetRow = document.createElement("tr")
headerNames.forEach((headerName) => {
planetRow.appendChild(
document.createElement("td")
)
})
planetRow.setAttribute("id", name)
table.appendChild(planetRow)
})
div.appendChild(table)
this.planetDisplayCreated = true
}
this.updatePlanetDisplay = function(planetData) {
planetData.forEach((d) => {
var content = (d.name, d.az, d.alt)
var planetRow = document.getElementById(d.name)
planetRow.childNodes.forEach((node, idx) => {
var contentFloat = parseFloat(content(idx))
if (isNaN(contentFloat)) {
node.textContent = content(idx)
} else {
node.textContent = contentFloat.toFixed(2)
}
})
})
}
this.initGeoLocationDisplay = function() {
this.geoLocationIds.forEach((id) => {
var node = document.getElementById(id)
node.childNodes(1).onkeyup = this.onGeoLocationKeyUp()
})
var appNode = document.getElementById("app")
var resetLocationButton = document.createElement("button")
resetLocationButton.setAttribute("id", "reset-location")
resetLocationButton.onclick = this.onResetLocationClick()
resetLocationButton.textContent = "Reset Geo Location"
appNode.appendChild(resetLocationButton)
}
this.updateGeoLocationDisplay = function() {
Object.keys(this.geoLocation).forEach((id) => {
var node = document.getElementById(id)
node.childNodes(1).value = parseFloat(
this.geoLocation(id)
).toFixed(2)
})
}
this.getDisplayedGeoLocation = function() {
var displayedGeoLocation = this.geoLocationIds.reduce((val, id) => {
var node = document.getElementById(id)
var nodeVal = parseFloat(node.childNodes(1).value)
val(id) = nodeVal
if (isNaN(nodeVal)) {
val.valid = false
}
return val
}, {valid: true})
return displayedGeoLocation
}
this.onGeoLocationKeyUp = function() {
return (evt) => {
var currentTime = new Date()
if (this.keyUpTimer !== null){
clearTimeout(this.keyUpTimer)
}
this.keyUpTimer = setTimeout(() => {
var displayedGeoLocation = this.getDisplayedGeoLocation()
if (displayedGeoLocation.valid) {
delete displayedGeoLocation.valid
this.geoLocation = displayedGeoLocation
console.log("Using user supplied geo location")
}
}, this.keyUpInterval)
}
}
this.onResetLocationClick = function() {
return (evt) => {
console.log("Geo location reset clicked")
this.getGeoLocation().then((coords) => {
this.geoLocation = this.processCoordinates(coords)
this.updateGeoLocationDisplay()
})
}
}
this.initUpdateTimer = function () {
if (this.updateTimer !== null) {
clearInterval(this.updateTimer)
}
this.updateTimer = setInterval(
this.update.bind(this),
this.updateInterval
)
return this.updateTimer
}
this.testPerformance = function(n) {
var t0 = performance.now()
var promises = ()
for (var i=0; i<n; i++) {
promises.push(this.getPlanetEphemeris("mars"))
}
Promise.all(promises).then(() => {
var delta = (performance.now() - t0)/1000
console.log(`Took ${delta.toFixed(4)} seconds to do ${n} requests`)
})
}
}
var app
document.addEventListener("DOMContentLoaded", (evt) => {
app = new App()
app.init()
})
این برنامه به طور دوره ای (هر 2 ثانیه) به روز رسانی و نمایش سیاره های گذرا. ما میتوانیم مختصات جغرافیایی خود را ارائه دهیم یا به Web Geolocation API اجازه دهیم موقعیت فعلی ما را تعیین کند. اگر کاربر تایپ کردن را برای نیم ثانیه یا بیشتر متوقف کند، برنامه موقعیت جغرافیایی را به روز می کند.
در حالی که این یک آموزش جاوا اسکریپت نیست، فکر میکنم برای درک کارهایی که بخشهای مختلف اسکریپت انجام میدهند مفید باشد:
createPlanetDisplay
به صورت پویا عناصر HTML را ایجاد می کند و آنها را به Document Object Model (DOM) متصل می کند.updatePlanetDisplay
داده های دریافتی از سرور را می گیرد و عناصر ایجاد شده توسط را پر می کندcreatePlanetDisplay
get
یک درخواست GET به سرور می دهد. را XMLHttpRequest شی اجازه می دهد تا این کار بدون بارگیری مجدد انجام شود page.post
یک درخواست POST به سرور می دهد. مانند باget
این کار بدون بارگیری مجدد انجام می شود page.getGeoLocation
استفاده می کند Web Geolocation API برای دریافت مختصات جغرافیایی فعلی کاربر. این باید “در یک زمینه امن” انجام شود (یعنی باید از آن استفاده کنیمHTTPS
نهHTTP
).getPlanetEphemeris
وgetPlanetEphemerides
برای دریافت ephemeris برای یک سیاره خاص و برای دریافت ephemerides برای همه سیارات به ترتیب درخواست های GET را به سرور ارائه دهید.testPerformance
باعث می شودn
به سرور درخواست می کند و تعیین می کند که چقدر طول می کشد.
آغازگر روی در حال اعزام به هروکو
هروکو سرویسی برای استقرار آسان برنامه های وب است. Heroku از پیکربندی مولفههای وب یک برنامه مانند پیکربندی پراکسیهای معکوس یا نگرانی در مورد تعادل بار مراقبت میکند. برای برنامههایی که درخواستهای کمی را مدیریت میکنند و تعداد کمی از کاربران، Heroku یک سرویس میزبانی رایگان عالی است.
استقرار برنامه های پایتون در Heroku در سال های اخیر بسیار آسان شده است. در هسته آن، ما باید دو فایل ایجاد کنیم که وابستگی های برنامه ما را فهرست کرده و به Heroku بگوییم که چگونه برنامه ما را اجرا کند.
آ پیپ فایل از اولی مراقبت می کند، در حالی که الف نمایه از دومی مراقبت می کند. یک Pipfile با استفاده از آن نگهداری می شود pipenv
– هر بار که یک وابستگی نصب می کنیم به Pipfile خود (و Pipfile.lock) اضافه می کنیم.
به منظور اجرای برنامه ما روی هروکو، ما باید یک وابستگی دیگر اضافه کنیم:
me@local:~/planettracker$ pipenv install gunicorn
ما می توانیم پروفیل خود را ایجاد کنیم و خط زیر را به آن اضافه کنیم:
web: gunicorn aiohttp_app:app --worker-class aiohttp.GunicornWebWorker
اساساً این به هروکو میگوید که از آن استفاده کند گونیکورن برای اجرای برنامه ما، با استفاده از ویژه aiohttp کارگر وب
قبل از اینکه بتوانید در Heroku مستقر شوید، باید برنامه را با Git ردیابی کنید:
me@local:~/planettracker$ git init
me@local:~/planettracker$ git add .
me@local:~/planettracker$ git commit -m "first commit"
اکنون می توانید دستورالعمل ها را دنبال کنید روی مرکز توسعه هروکو اینجا برای استقرار برنامه شما توجه داشته باشید که میتوانید مرحله «آماده کردن برنامه» این آموزش را نادیده بگیرید، زیرا از قبل یک برنامه git tracked دارید.
هنگامی که برنامه شما مستقر شد، می توانید به URL انتخابی Heroku در مرورگر خود بروید و برنامه را مشاهده کنید، که چیزی شبیه به این خواهد بود:
نتیجه
در این مقاله، به این موضوع پرداختیم که توسعه وب ناهمزمان در پایتون چگونه است – مزایا و کاربردهای آن. پس از آن، ما یک واکنشگر ساده ساختیم aiohttp برنامه مبتنی بر این است که به صورت پویا مختصات آسمان مربوط به سیارات منظومه شمسی را با توجه به مختصات جغرافیایی کاربر نمایش می دهد.
پس از ساخت برنامه، ما آن را برای استقرار آماده کرده ایم روی هیروکو.
همانطور که قبلا ذکر شد، شما می توانید هر دو را پیدا کنید کد منبع و نسخه ی نمایشی برنامه در صورت نیاز
(برچسبها به ترجمه)# python
منتشر شده در 1403-01-26 10:56:03