વ્યુ કાઉન્ટ એ પહેલી સંખ્યા છે જેના પર મુલાકાતી વિશ્વાસ કરે છે. તે તેમને જણાવે છે કે વિડિયો તેમના ત્રીસ સેકન્ડ કે ત્રીસ મિનિટના સમયને લાયક છે કે નહીં. TopVideoHub પર, તે સંખ્યા ઝડપથી બદલાય છે. એક ટ્રેન્ડિંગ ક્લિપ દસ મિનિટમાં 40,000 વ્યુઝ મેળવી શકે છે. જો પેજ પર કાઉન્ટર સ્થિર (freeze) થઈ જાય, તો રૂમ ખાલી હોવા જેવો અનુભવ થાય છે. યુઝર્સ સાઇટ છોડી દે છે.
તે સંખ્યાને બ્રાઉઝર સુધી પહોંચાડવી ખૂબ જ સરળ લાગે છે. પણ તે નથી. મોટાભાગની ટીમો જે પહેલો ઉકેલ અપનાવે છે તે છે polling. તેને સેટ કરવું સરળ છે અને તે staging માં બરાબર કામ કરે છે. પરંતુ staging છેતરે છે.
જ્યારે Polling તમારી સામે જ DDoS બની જાય
TopVideoHub ની ટીમે એક સરળ JavaScript poller બનાવ્યો. તે દર પાંચ સેકન્ડે લેટેસ્ટ વ્યુ કાઉન્ટ મેળવતું હતું. ત્રણ બ્રાઉઝર ખુલ્લા હોય તેવા ટેસ્ટ એન્વાયરમેન્ટમાં તે ખૂબ સરસ લાગતું હતું. પરંતુ production માં, તેણે પ્લેટફોર્મને તબાહ કરી દીધું.
આઠ હજાર એકસાથે જોનારા (concurrent) વ્યુઅર્સ દર પાંચ સેકન્ડે રિફ્રેશ કરતા હતા, જેના કારણે પ્રતિ સેકન્ડ 1,600 રિક્વેસ્ટ જનરેટ થતી હતી. દરેક રિક્વેસ્ટ ડેટાબેઝમાં ઊંડે સુધી જતી હતી. Replication lag વધી ગયો. Read replicas પર ભારે ભાર પડ્યો. Cache લેયર્સ બાયપાસ થઈ ગયા. ટીમ વિડિયો સર્વ કરી રહી નહોતી, તેઓ પોતાની જાતે જ પેદા કરેલો લોડ સર્વ કરી રહ્યા હતા.
જ્યાં સુધી સમસ્યા ન સર્જાય ત્યાં સુધી polling સુરક્ષિત લાગે છે. લો-ટ્રાફિક ડેશબોર્ડ્સ અથવા એડમિન પેનલ્સ માટે તે ઠીક છે. પરંતુ વાયરલ વિડિયો પેજ માટે, તે એક ટિકિંગ બોમ્બ છે. ટીમને સર્વરથી બ્રાઉઝર સુધી એક પર્સિસ્ટન્ટ પાઇપની જરૂર હતી, પરંતુ તેમને full-duplex પ્રોટોકોલની જટિલતાની જરૂર નહોતી.
શા માટે SSE વન-વે પાઇપ્સ માટે યોગ્ય છે
Server-Sent Events (SSE) બરાબર આ પ્રકારની સમસ્યા માટે જ બનાવવામાં આવ્યું છે: સર્વર પાસે ડેટા છે, અને બ્રાઉઝરને ફક્ત સાંભળવાની જરૂર છે.
WebSockets થી વિપરીત, SSE સાદા HTTP પર કામ કરે છે. આ બાબત જેટલી સાંભળવામાં લાગે છે તેના કરતા વધુ મહત્વની છે. તમારે નવા પ્રોક્સી રૂલ્સ, અપગ્રેડ હેડર્સ અથવા લોડ-બેલેન્સરની જટિલતાની જરૂર નથી. જો તમારું સર્વર HTTP/1.1 અથવા HTTP/2 વાપરતું હોય, તો SSE કામ કરશે. ડીબગિંગ સરળ છે કારણ કે સ્ટ્રીમ ફક્ત ટેક્સ્ટ છે. તમે curl ને એન્ડપોઈન્ટ પર પોઈન્ટ કરી શકો છો અને રીઅલ ટાઇમમાં નંબરો ફરતા જોઈ શકો છો, જે બાઈનરી સોકેટ ફ્રેમમાં શું ભૂલ થઈ છે તે અનુમાન લગાવવા કરતા ઘણું સારું છે.
બ્રાઉઝર કંટાળાજનક ભાગો મફતમાં સંભાળી લે છે. જો કનેક્શન તૂટી જાય, તો SSE Last-Event-ID હેડર સાથે આપમેળે ફરીથી કનેક્ટ થાય છે જેથી સર્વરને ખબર પડે કે ક્યાંથી ફરી શરૂ કરવું. JavaScript માં API સપાટી ખૂબ જ નાની છે: એક EventSource બનાવો, onmessage હેન્ડલર જોડો, અને તમારું કામ પૂરું.
Caching એ સાચી આર્કિટેક્ચર છે
લાઇવ કાઉન્ટર્સમાં સૌથી મોટી આર્કિટેક્ચરલ ભૂલ એ છે કે દરેક બ્રાઉઝર કનેક્શનને ડેટાબેઝ ક્વેરી કરવાના કારણ તરીકે ગણવું. જો 8,000 લોકો એક જ વિડિયો જોતા હોય, તો દર બે સેકન્ડે 8,000 ક્વેરી ચલાવવી એ પાગલપન છે. તમારો ડેટાબેઝ વાયરલ ટ્રાફિક ટકી શકશે નહીં.
TopVideoHub એ આ સમસ્યાનું સમાધાન APCu દ્વારા કર્યું, જે PHP નું in-memory opcode અને user cache છે. આ પ્રક્રિયા સરળ છે. એક બેકગ્રાઉન્ડ પ્રોસેસ—અથવા ટાઈમર પર હળવું (lightweight) એન્ડપોઈન્ટ હિટ—દર બે સેકન્ડે એકવાર APCu માં વર્તમાન વ્યુ કાઉન્ટ લખે છે. SSE એન્ડપોઈન્ટ, જેને હજારો બ્રાઉઝર્સ ખુલ્લું રાખી શકે છે, તે ફક્ત APCu માંથી જ વાંચે છે.
પરિણામ: ગમે તેટલા વ્યુઅર્સ જોતા હોય, ડેટાબેઝ પર દર બે સેકન્ડે માત્ર એક જ રિક્વેસ્ટ જાય છે. કેશ (cache) એક શોક એબ્સોર્બર તરીકે કામ કરે છે. APCu કોઈ અજાણી વસ્તુ નથી. તે PHP સાથે આવે છે, શેર્ડ મેમરીમાં રહે છે, અને કોઈપણ નેટવર્ક રાઉન્ડ-ટ્રિપ કરતા ઝડપથી વાંચે છે. વારંવાર બદલાતી પરંતુ તરત જ નહીં, તેવી એક સંખ્યા માટે તે યોગ્ય સાધન છે.
જો તમે APCu નો ઉપયોગ નથી કરી રહ્યા, તો Redis અથવા Memcached પણ કામ કરશે. સિદ્ધાંત સમાન જ રહે છે. ડેટાબેઝથી હોટ રીડ પાથ (hot read path) ને અલગ કરો.
PHP, LiteSpeed અને Cloudflare ને સ્ટ્રીમિંગ માટે તૈયાર કરવા
PHP કામ પૂરું કરીને ઘરે જવા માંગે છે. વેબ સર્વર્સ આઉટપુટ બફર કરવા અને વ્યવસ્થિત રિસ્પોન્સ આપવા માંગે છે. SSE ને તેનાથી વિપરીત જરૂર છે: એક એવું કનેક્શન જે ખુલ્લું રહે અને બાઈટ્સ આવે તેમ તેને ફ્લશ (flush) કરતું રહે. જો કાળજી ન રાખવામાં આવે, તો તમારો "સ્ટ્રીમ" ત્રીસ સેકન્ડ પછી એક જ મોટા બ્લોબ તરીકે આવશે, જેનો હેતુ નિષ્ફળ જશે.
અહીં TopVideoHub એ પાઇપને અટક્યા વગર કેવી રીતે ચાલુ રાખી તે છે:
આઉટપુટ બફરિંગ બંધ કરો. SSE સ્ક્રિપ્ટની શરૂઆતમાં, PHP દ્વારા સક્ષમ કરવામાં આવેલા બફરિંગના દરેક લેયરને ડિસેબલ કરો. જો બફર સક્રિય હોય તો ob_end_flush() કોલ કરો, અને તમારા હેડર્સ મોકલ્યા પછી ob_implicit_flush(true) સાથે ઇમ્પ્લીસિટ ફ્લશિંગ બંધ કરો.
પ્રોક્સીઝને પાછળ હટવા કહો. X-Accel-Buffering: no હેડર મોકલો. Nginx તેને સાંભળે છે. LiteSpeed તેને સાંભળે છે. તે સંકેત આપે છે કે રિસ્પોન્સને બફર અથવા કેશે કરી શકાય તેવા મોટા ટુકડામાં કમ્પ્રેસ ન કરવો જોઈએ.
ટૂંકો સમયગાળો સેટ કરો. દરેક SSE કનેક્શન એક PHP વર્કરને રોકી રાખે છે. TopVideoHub સ્ટ્રીમ્સને 55 સેકન્ડ સુધી મર્યાદિત રાખે છે. જ્યારે ટાઈમર પૂરો થાય, ત્યારે સર્વર એક અંતિમ કોમેન્ટ મોકલે છે, સ્ટ્રીમ બંધ કરે છે, અને બ્રાઉઝર આપમેળે ફરીથી કનેક્ટ થાય છે. આ રીકનેક્શન એક નવા વર્કર પર જાય છે, જે કોઈપણ સિંગલ પ્રોસેસને કાયમ માટે રોકાઈ રહેતા અટકાવે છે.
જીવંત રહેવા માટે પિંગ (Ping) કરો. દર વીસ સેકન્ડે એક કોમેન્ટ લાઇન મોકલો—જેમ કે : ping. SSE માં કોમેન્ટ્સને બ્રાઉઝરના મેસેજ હેન્ડલર દ્વારા અવગણવામાં આવે છે, પરંતુ તે TCP કનેક્શનને સક્રિય (warm) રાખે છે. લોડ બેલેન્સર્સ અને CDNs ઘણીવાર ત્રીસ કે સાઠ સેકન્ડ પછી શાંત કનેક્શન્સને કાપી નાખે છે. એક નાનકડી ન્યૂલાઇન (newline) તમને તે મુશ્કેલીથી બચાવી શકે છે.
યુઝરના ટેબ (tab) નો આદર કરો. જ્યારે કોઈ મુલાકાતી ટેબને મિનિમાઇઝ કરે અથવા છુપાવે, ત્યારે કનેક્શન બંધ કરી દો. બ્રાઉઝરમાં visibilitychange પર ધ્યાન આપો અને eventSource.close() કોલ કરો. સર્વરે પણ ક્લાયન્ટ ડિસ્કનેક્ટ થાય તે પારખવું જોઈએ અને લૂપ (loop) સમાપ્ત કરવું જોઈએ. PHP લૂપની અંદર connection_aborted() દ્વારા તપાસ કરી શકે છે. જે લોકો દસ મિનિટ પહેલા જ જતી રહ્યા હોય, તેમના માટે 'ઘોસ્ટ કનેક્શન્સ' (ghost connections) દ્વારા વર્કર્સનો બગાડ ન કરો.
કડક મર્યાદા: PHP વર્કર્સ
PHP માં SSE તેની મર્યાદાઓ વિશે સ્પષ્ટ છે. દરેક ખુલ્લું SSE કનેક્શન એક PHP વર્કરનો ઉપયોગ કરે છે. જો તમારા પૂલમાં સો વર્કર્સ હોય, તો તમારી પાસે સો સ્ટ્રીમ્સ છે. બસ, આટલું જ. જ્યાં સુધી તમે Apache અથવા PHP-FPM પ્રોસેસ મોડેલની અંદર છો, ત્યાં સુધી કોઈ એસિંક (async) ઉપાય નથી. તમે pm.max_children ને ટ્યુન કરી શકો છો, પરંતુ મેમરી અને CPU વાસ્તવિક સીમા નક્કી કરે છે.
જો તમે નિયમિત પેજ લોડ્સ, API કોલ્સ અને એસેટ જનરેશન માટે પણ વર્કર્સનો ઉપયોગ કરી રહ્યા હોવ, તો આ મર્યાદા ઝડપથી આવી જાય છે. તમારા વર્કર સેચ્યુરેશન (saturation) પર ધ્યાનથી નજર રાખો. જો તમારા બધા વર્કર્સ વીસ મિનિટની સ્ટ્રીમ્સમાં ફસાયેલા હોવાને કારણે તમારો SSE એન્ડપોઇન્ટ ક્યુઇંગ (queuing) કરવા લાગે, તો તમારી આખી સાઇટ ધીમી પડી જશે.
જ્યારે આ સંખ્યાઓ પૂરતી ન રહે, ત્યારે આગળ વધો. Go એ સામાન્ય રીતે આગલું પગલું છે, જોકે Rust, Node.js અથવા Erlang પણ તે જ ભૂમિકા ભજવી શકે છે. Go ના goroutines એ મુખ્ય ચાવી છે. એક goroutine માં માત્ર થોડા કિલોબાઇટ્સનો જ ખર્ચ થાય છે. તમે સામાન્ય હાર્ડવેર પર પણ કોઈપણ મુશ્કેલી વગર હજારો સ્ટ્રીમ્સ ચલાવી શકો છો. મુખ્ય લોજિક સમાન રહે છે—કેશમાંથી વાંચો, સોકેટમાં લખો—પરંતુ રનટાઇમ ભારે પ્રોસેસથી હળવા વજનના થ્રેડ્સ (threads) માં બદલાઈ જાય છે.
જોકે, ત્યાંથી શરૂઆત ન કરો. PHP તમને આશ્ચર્યજનક રીતે ઘણું આગળ લઈ જશે. પહેલા પ્રોડક્ટને વેલિડેટ કરો. જ્યારે મેટ્રિક્સ પેજ ડેટાબેઝ ઓવરલોડને બદલે વર્કર એક્ઝોસ્ટિયન (worker exhaustion) બતાવે, ત્યારે સમજી લેવું કે તમે આ સ્ટેકથી આગળ વધી ગયા છો. આ એક સારી સમસ્યા છે.
મુખ્ય વાત (Takeaway)
લાઈવ કાઉન્ટર્સ માત્ર ટેકનોલોજી વિશે નથી. તે તમારા ડેટાબેઝને તમારા પોતાના યુઝર્સથી બચાવવા વિશે છે. SSE થી શરૂઆત કરો કારણ કે તે દેખાવ કરતા વધુ સરળ છે. સ્ટ્રીમ અને ડેટાબેઝ વચ્ચે આક્રમક રીતે કેશ (cache) નો ઉપયોગ કરો જેથી કનેક્શનની સંખ્યા ક્વેરીની સંખ્યા ન બની જાય. તમારા વર્કર લિમિટ પર તીક્ષ્ણ નજર રાખો. અને સરળતાથી શરૂઆત કરો. જ્યાં સુધી જરૂર ન પડે ત્યાં સુધી PHP પૂરતું છે, અને ત્યાં સુધી તમે ચોક્કસપણે જાણશો કે તમે શા માટે ફરીથી લખી રહ્યા છો.
તમારા આગામી પ્રોજેક્ટ માટે:
- જ્યારે ડેટા એક જ દિશામાં, સર્વરથી બ્રાઉઝર તરફ વહેતો હોય ત્યારે SSE નો ઉપયોગ કરો.
- ડેટાબેઝની આગળ કેશ લેયર (cache layer) મૂકો. દર થોડી સેકન્ડે એક ક્વેરી હજારો ક્વેરી કરતા વધુ સારી છે.
- SSE કનેક્શન્સને એક મિનિટથી ઓછી સમયગાળા માટે મર્યાદિત કરો અને બ્રાઉઝરને ફરીથી કનેક્ટ થવા દો.
- ટેબ છુપાવતી વખતે સ્ટ્રીમ્સ બંધ કરો. નિષ્ક્રિય (idle) કનેક્શન્સ માટે લાઈવ વર્કર્સનો બગાડ ન કરો.
- PHP વર્કર વપરાશ પર નજર રાખો. જ્યારે તમે તેની મહત્તમ મર્યાદા પર પહોંચી જાઓ, ત્યારે સ્ટ્રીમિંગ લેયરને Go માં પોર્ટ કરો.
