نحوه استفاده از رویدادهای ارسال شده توسط سرور در Node.js
![](https://khabarkaav.ir/wp-content/uploads/2024/02/1708998285server-sent-events.jpg)
![](https://uploads.sitepoint.com/wp-content/uploads/2024/02/1708998285server-sent-events.jpg)
در این مقاله، نحوه استفاده از رویدادهای ارسال شده توسط سرور را تحلیل خواهیم کرد تا یک کلاینت از طریق اتصال HTTP بهروزرسانیهای خودکار را از سرور دریافت کند. ما همچنین به این خواهیم پرداخت که چرا این مفید است، و نمایش های عملی از نحوه استفاده از رویدادهای ارسال شده توسط سرور با Node.js را نشان خواهیم داد.
چرا رویدادهای ارسال شده توسط سرور مفید هستند؟
وب بر اساس پیام های HTTP درخواست-پاسخ است. مرورگر شما درخواست URL می دهد و سرور با داده پاسخ می دهد. این ممکن است منجر به درخواستهای بیشتر مرورگر و پاسخهای سرور برای تصاویر، CSS، جاوا اسکریپت و غیره شود. سرور نمیتواند پیامها را به مرورگر آغاز کند ، پس چگونه میتواند نشان دهد که دادهها تغییر کردهاند؟ خوشبختانه، میتوانید آپشن های ی مانند بولتنهای خبری زنده، گزارشهای آبوهوا و قیمت سهام را با رویدادهای ارسالشده توسط سرور اضافه کنید.
پیاده سازی به روز رسانی داده های زنده با استفاده از فناوری های استاندارد وب همیشه امکان پذیر بوده است:
وب دهه 1990 از یک به روز رسانی تمام صفحه یا فریم/آیفریم استفاده می کرد.
وب دهه 2000 Ajax را معرفی کرد که میتوانست از نظرسنجی طولانی برای درخواست داده و بهروزرسانی عناصر DOM مناسب با اطلاعات جدید استفاده کند.
هیچیک از گزینهها ایدهآل نیستند، زیرا مرورگر باید بهروزرسانی را راهاندازی کند. اگر به دفعات درخواست کند، هیچ داده ای تغییر نخواهد کرد، پس هر دو مرورگر و سرور کارهای غیر ضروری انجام می دهند. اگر درخواستها را خیلی آهسته ارسال کند، ممکن است یک بهروزرسانی مهم را از دست بدهد و قیمت سهامی که تماشا میکنید قبلاً سقوط کرده است!
رویدادهای ارسال شده توسط سرور (SSE) به سرور اجازه میدهد تا دادهها را در هر زمان به مرورگر فشار دهد:
مرورگر همچنان درخواست اولیه برای برقراری اتصال را ارائه می دهد.
سرور یک پاسخ جریان رویداد را برمیگرداند و اتصال را باز نگه میدارد.
سرور می تواند از این اتصال برای ارسال پیام های متنی در هر نقطه استفاده کند.
داده های دریافتی یک رویداد جاوا اسکریپت را در مرورگر ایجاد می کند. یک تابع کنترل کننده رویداد می تواند داده ها را تجزیه کرده و DOM را به روز کند.
در اصل، SSE یک جریان بی پایان از داده ها است. آن را به عنوان دانلود یک فایل بی نهایت بزرگ در تکه های کوچک در نظر بگیرید که می توانید آن را رهگیری و بخوانید.
SSE اولین بار در سال 2006 پیاده سازی شد و تمام مرورگرهای اصلی از این استاندارد پشتیبانی می کنند. احتمالاً کمتر از WebSockets شناخته شده است، اما رویدادهای ارسال شده توسط سرور ساده تر هستند، از HTTP استاندارد استفاده می کنند، از ارتباطات یک طرفه پشتیبانی می کنند و اتصال مجدد خودکار را ارائه می دهند. این آموزش نمونه کد Node.js را بدون ماژول های شخص ثالث ارائه می دهد، اما SSE در سایر زبان های سمت سرور از جمله PHP در دسترس است.
رویدادهای ارسال شده توسط سرور شروع سریع
نمایش زیر یک وب سرور Node.js را پیاده سازی می کند که یک عدد تصادفی بین 1 تا 1000 را در یک بازه تصادفی حداقل هر سه ثانیه یک بار خروجی می دهد.
در زیر نمایش Node.js SSE ما آمده است (در صورت تمایل می توانید آن را در یک برگه مرورگر جداگانه باز کنید).
کد از ماژول های http
و url
استاندارد Node.js برای ایجاد یک وب سرور و تجزیه URL ها استفاده می کند:
import http from "node:http" ; import url from "node:url" ;
سرور درخواست URL ورودی را تحلیل می کند و هنگامی که با یک مسیر /random
مواجه می شود واکنش نشان می دهد:
const port = 8000 ; http . createServer ( async ( req , res ) => { const uri = url . parse ( req . url ) . pathname ; switch ( uri ) { case "/random" : sseStart ( res ) ; sseRandom ( res ) ; break ; } } ) . listen ( port ) ; console . log ( ` server running: http://localhost: ${ port } \n\n ` ) ;
در ابتدا با هدر جریان رویداد SSE HTTP پاسخ می دهد:
function sseStart ( res ) { res . writeHead ( 200 , { Content - Type : "text/event-stream" , Cache - Control : "no-cache" , Connection : "keep-alive" } ) ; }
سپس یک تابع دیگر یک عدد تصادفی می فرستد و پس از سپری شدن یک بازه تصادفی خود را فرا می خواند:
function sseRandom ( res ) { res . write ( "data: " + ( Math . floor ( Math . random ( ) * 1000 ) + 1 ) + "\n\n" ) ; setTimeout ( ( ) => sseRandom ( res ) , Math . random ( ) * 3000 ) ; }
اگر کد را به صورت محلی اجرا می کنید، می توانید پاسخ را با استفاده از cURL در ترمینال خود آزمایش کنید:
$ > curl -H Accept:text/event-stream http://localhost:8000/random data: 481 data: 127 data: 975
Ctrl | Cmd و C برای خاتمه درخو است.
جاوا اسکریپت سمت کلاینت مرورگر با استفاده از سازنده شی EventSource به URI /random
متصل می شود:
const source = new EventSource ( "/random" ) ;
داده های ورودی یک کنترل کننده رویداد message
راه اندازی می کند که در آن رشته data:
در ویژگی .data
شی رویداد موجود است:
source . addEventListener ( 'message' , e => { console . log ( 'RECEIVED' , e . data ) ; } ) ;
یادداشت های مهم
مانند Fetch()، مرورگر یک درخواست استاندارد HTTP میدهد، پس ممکن است لازم باشد CSP، CORS را مدیریت کنید و به صورت اختیاری یک آرگومان دوم { withCredentials: true }
به سازنده EventSource
ارسال کنید تا کوکیها را ارسال کنید.
سرور باید برای هر کاربر متصل، اشیاء پاسخ res
منفرد را حفظ کند تا به آنها داده ارسال کند. در کد بالا با انتقال مقدار در بسته شدن به تماس بعدی به دست می آید.
دادههای پیام فقط میتوانند رشتهای باشند (شاید JSON) که در قالب data: <message>\n\n
. بازده حمل پایانی ضروری است.
سرور می تواند پاسخ SSE را در هر زمان با res.end()
خاتمه دهد، اما…
هنگامی که یک قطع ارتباط رخ می دهد، مرورگر به طور خودکار سعی می کند دوباره وصل شود. نیازی به نوشتن کد اتصال مجدد خود نیست.
رویدادهای پیشرفته ارسال شده توسط سرور
SSE به کد بیشتری از آنچه در بالا نشان داده شده است نیاز ندارد، اما در بخشهای زیر گزینههای بیشتر مورد بحث قرار میگیرد.
یکی در مقابل بسیاری از کانال های SSE
یک سرور می تواند هر تعداد URL کانال SSE را ارائه دهد. مثلا:
/latest/news
/latest/weather
/latest/stockprice
اگر یک صفحه یک موضوع را نشان دهد، ممکن است عملی باشد، اما اگر یک صفحه اخبار، آب و هوا و قیمت سهام را نشان دهد، کمتر عملی است. در چنین شرایطی، سرور باید سه اتصال را برای هر کاربر حفظ کند، که می تواند با افزایش ترافیک منجر به مشکلات حافظه شود.
یک گزینه جایگزین، ارائه یک URL نقطه پایانی واحد است، مانند /latest
، که هر نوع داده ای را در یک کانال ارتباطی ارسال می کند. مرورگر می تواند موضوعات مورد علاقه را در رشته جستجوی URL نشان دهد - به عنوان مثال، /latest?type=news,weather,stockprice
- پس سرور می تواند پاسخ های SSE را به پیام های خاص محدود کند.
ارسال داده های مختلف در یک کانال
پیامهای سرور میتوانند یک event:
در خط بالای data:
برای شناسایی انواع خاصی از اطلاعات:
event: news data: SSE is great! event: weather data: { "temperature": "20C", "wind": "10Kph", "rain": "25%" } event: stock data: { "symbol": "AC", "company": "Acme Corp", "price": 123.45, "increase": -1.1 }
اینها کنترل کننده رویداد "message"
سمت سرویس گیرنده را فعال نمی کنند. باید برای هر نوع event
کنترل کننده هایی اضافه کنید. مثلا:
source . addEventListener ( 'news' , e => { document . getElementById ( 'headline' ) . textContent = e . data ; } ) ; source . addEventListener ( 'weather' , e => { const w = JSON . parse ( e . data ) ; document . getElementById ( 'weather' ) . textContent = ` ${ w . temperature } with ${ w . wind } wind ` ; } ) ; source . addEventListener ( 'stock' , e => { const s = JSON . parse ( e . data ) ; document . getElementById ( ` stock- ${ s . symbol } ` ) . textContent = ` ${ s . share } : ${ s . price } ( ${ s . increase } %) ` ; } ) ;
استفاده از شناسه های داده
در صورت تمایل، سرور همچنین می تواند یک id:
پس از خط data:
ارسال کند:
event: news data: SSE is great! id: 42
اگر اتصال قطع شود، مرورگر آخرین id
در هدر HTTP Last-Event-ID
به سرور برمیگرداند تا سرور بتواند پیامهای از دست رفته را دوباره ارسال کند.
آخرین شناسه نیز در سمت سرویس گیرنده در ویژگی .lastEventId
شی رویداد موجود است:
source . addEventListener ( 'news' , e => { console . log ( ` last ID: ${ e . lastEventId } ` ) ; document . getElementById ( 'headline' ) . textContent = e . data ; } ) ;
تعیین تاخیرهای تلاش مجدد
اگرچه اتصال مجدد خودکار است، سرور شما ممکن است بداند که داده های جدیدی برای یک دوره خاص مورد انتظار نیست، پس نیازی به حفظ یک کانال ارتباطی فعال نیست. سرور میتواند به تنهایی یا بهعنوان بخشی از پیام نهایی، یک پاسخ retry:
با مقدار میلیثانیه ارسال کند. مثلا:
retry: 60000 data: Please don't reconnect for another minute!
به محض دریافت، مرورگر اتصال SSE را قطع میکند و پس از سپری شدن مدت زمان تأخیر سعی در اتصال مجدد میکند.
سایر گردانندگان رویداد
علاوه بر "message"
و رویدادهای نامگذاری شده، میتوانید کنترلکنندههای "open"
و "error"
را در جاوا اسکریپت سمت کلاینت خود ایجاد کنید.
هنگامی که اتصال سرور برقرار می شود، یک رویداد "open"
می شود. می توان از آن برای اجرای کد پیکربندی اضافی یا مقداردهی اولیه عناصر DOM استفاده کرد:
const source = new EventSource ( '/sse1' ) ; source . addEventListener ( 'open' , e => { console . log ( 'SSE connection established.' ) ; } ) ;
یک رویداد "error"
زمانی ایجاد می شود که اتصال سرور از کار بیفتد یا خاتمه یابد. میتوانید ویژگی .eventPhase
شی رویداد را تحلیل کنید تا ببینید چه اتفاقی افتاده است:
source . addEventListener ( 'error' , e => { if ( e . eventPhase === EventSource . CLOSED ) { console . log ( 'SSE connection closed' ) ; } else { console . log ( 'error' , e ) ; } } ) ;
به یاد داشته باشید، نیازی به اتصال مجدد نیست: به طور خودکار اتفاق می افتد .
قطع ارتباط SSE
مرورگر می تواند یک ارتباط SSE را با استفاده از روش .close()
شی EventSource خاتمه دهد. مثلا:
const source = new EventSource ( '/sse1' ) ; setTimeout ( ( ) => source . close ( ) , 3_600_000 ) ;
سرور می تواند اتصال را با موارد زیر قطع کند:
شلیک res.end()
یا ارسال retry:
تاخیر، سپس
هنگامی که همان مرورگر سعی در اتصال مجدد دارد، وضعیت HTTP 204 را برمی گرداند.
فقط مرورگر می تواند با ایجاد یک شیء EventSource
جدید یک اتصال را دوباره برقرار کند.
نتیجه
رویدادهای جانبی سرور راهی برای پیادهسازی بهروزرسانیهای صفحه زنده ارائه میکنند که احتمالاً آسانتر، کاربردیتر و سبکتر از نظرسنجی Ajax مبتنی بر Fetch()
است. پیچیدگی در انتهای سرور است. شما باید:
اما این کاملاً تحت کنترل شماست و مقیاس بندی نباید پیچیده تر از هر برنامه وب دیگری باشد.
تنها اشکال این است که SSE به شما اجازه ارسال پیام از مرورگر به سرور را نمی دهد (به غیر از درخواست اتصال اولیه). شما می توانید از Ajax استفاده کنید، اما این برای برنامه هایی مانند بازی های اکشن بسیار کند است. برای ارتباط دو طرفه مناسب، به WebSockets نیاز دارید. ما به زودی یک آموزش جدید در مورد آن خواهیم داشت!
ارسال نظر