Idadi ya watazamaji ndiyo namba ya kwanza ambayo mgeni huiamini. Inawaambia ikiwa video inastahili sekunde tatu mapacha au dakika tatu puluh ya muda wao. Katika TopVideoHub, namba hiyo inasogea kwa kasi sana. Klipu inayovuma inaweza kupata watazamaji 40,000 ndani ya dakika kumi. Ikiwa kionyeshi kwenye ukurasa kinakwama, mazingira yanahisi kuwa tupu. Watumiaji huondoka mara moja.
Kufikisha namba hiyo kwenye kivinjari (browser) kunaonekana kuwa jambo rahisi. Lakini si hivyo. Suluhisho la kwanza ambalo timu nyingi hutumia ni polling. Ni rahisi kuunganisha na hufanya kazi vizuri katika mazingira ya staging. Lakini staging hutoa picha ya uongo.
Wakati Polling Inapokuwa DDoS Dhidi Yako Mwenyewe
Timu ya TopVideoHub ilitengeneza poller rahisi ya JavaScript. Ilikuwa inachukua idadi ya watazamaji ya hivi punde kila baada ya sekunde tano. Katika mazingira ya majaribio (test environment) yakiwa na vivinjari vitatu wazi, ilionekana vizuri sana. Lakini kwenye uzalishaji (production), iliangusha jukwaa kabisa.
Watazamaji elfu nane wanaotazama kwa wakati mmoja wakifanya refresh kila baada ya sekunde tano, walizalisha maombi (requests) 1,600 kwa sekunde. Kila ombi lilichimba ndani ya kanzidata (database). Ucheleweshaji wa replication (replication lag) ulipanda kwa kasi. Read replicas zilianza kutaabika. Tabaka za cache zilipitwa. Timu haikuwa ikihudumia video. Walikuwa wanajiletea mzigo wa kazi wenyewe.
Polling haina hatia mpaka pale inapokuwa na tatizo. Kwa dashboard zenye trafiki ndogo au paneli za admin, ni sawa. Lakini kwa ukurasa wa video inayovuma, ni bomu linalosubiri kulipuka. Timu ilihitaji njia ya kudumu (persistent pipe) kutoka kwenye seva hadi kwenye kivinjari, lakini hawakuhitaji utata wa itifaki ya full-duplex.
Kwa Nini SSE Inafaa kwa Njia za Upande Mmoja
Server-Sent Events (SSE) imeundwa kwa ajili ya aina hii ya tatizo: seva ina data, na kivinjari kinahitaji kusikiliza tu.
Tofauti na WebSockets, SSE inatumia HTTP ya kawaida. Hilo ni jambo muhimu zaidi kuliko linavyoonekana. Huhitaji kanuni mpya za proxy, vichwa vya habari vya upgrade (upgrade headers), au michezo ya load-balancer. Ikiwa seva yako inatumia HTTP/1.1 au HTTP/2, SSE inafanya kazi. Kutatua matatizo (debugging) ni rahisi kwa sababu mtiririko (stream) ni maandishi tu. Unaweza kutumia curl kwenye endpoint na kuona namba zikibadilika kwa wakati halisi (real time), jambo ambalo ni bora kuliko kukisia kwa nini binary socket frame ilikwama.
Kivinjari hushughulikia sehemu ngumu bila malipo. Ikiwa muunganisho utakatika, SSE inajiunganisha tena kiotomatiki kwa kutumia kichwa cha habari cha Last-Event-ID ili seva ijue wapi pa kuendelea. Uso wa API katika JavaScript ni mdogo sana: tengeneza EventSource, ambatisha mshikaji (handler) wa onmessage, na umemaliza.
Caching Ndiyo Msingi wa Kweli wa Usanifu
Kosa kubwa la usanifu katika vihesabu vya moja kwa moja (live counters) ni kuchukulia kila muunganisho wa kivinjari kama sababu ya kuuliza kanzidata. Ikiwa watu 8,000 wanatazama video ile ile, kuendesha maombi (queries) 8,000 kila baada ya sekunde mbili ni wazimu. Kanzidata yako haitastahimili trafiki ya video inayovuma.
TopVideoHub ilitatua hili kwa kutumia APCu, cache ya PHP ya ndani ya kumbukumbu (in-memory opcode and user cache). Mtiririko ni rahisi. Mchakato wa nyuma (background process)—au endpoint nyepesi inayofanya kazi kwa ratiba—huandika idadi ya watazamaji ya sasa kwenye APCu kila baada ya sekunde mbili. Endpoint ya SSE, ambayo maelfu ya vivinjari yanaweza kuwa imefunguliwa, inasoma moja kwa moja kutoka kwenye APCu.
Matokeo yake: kanzidata inapata ombi moja tu kila baada ya sekunde mbili, bila kujali ni watazamaji wangapi wapo mtandaoni. Cache inakuwa kama kifaa cha kupunguza mshtuko (shock absorber). APCu si kitu cha ajabu. Inakuja pamoja na PHP, inaishi kwenye kumbukumbu ya pamoja (shared memory), na inasoma kwa kasi kuliko safari yoyote ya mtandao (network round-trip). Kwa namba moja inayobadilika mara kwa mara lakini si papo hapo, ndicho kifaa sahihi.
Ikiwa hautumii APCu, Redis au Memcached pia zinafanya kazi. Kanuni ni ile ile. Tenganisha njia ya kusoma data inayotumika sana (hot read path) kutoka kwenye kanzidata.
Kushawishi PHP, LiteSpeed, na Cloudflare Kufanya Stream
PHP inataka kumaliza kazi na kwenda nyumbani. Web servers zinataka kuzuia matokeo (buffer output) na kutoa jibu safi. SSE inahitaji kinyume chake: muunganisho unaobaki wazi, ukimimina data (bytes) zinapowadia. Bila uangalifu, "stream" yako itafika kama kizuizi kimoja kikubwa sekunde tatu puluh baadaye, jambo ambalo linaharibu lengo.
Hivi ndivyo TopVideoHub ilivyofanya ili njia isizibe.
Zima output buffering. Mwanzoni mwa skripti ya SSE, zima kila tabaka la buffering ambalo PHP inaweza kuwa imewasha. Itia wito ob_end_flush() ikiwa buffer ipo hai, na uzime flushing ya moja kwa moja kwa kutumia ob_implicit_flush(true) baada ya vichwa vya habari (headers) kutumwa.
Iambie proxy zikubali. Tuma kichwa cha habari cha X-Accel-Buffering: no. Nginx inaisikiliza. LiteSpeed inaisikiliza. Inatoa ishara kwamba jibu halipaswi kuzuiwa (buffered) au kubanwa (compressed) kuwa kizuizi kinachoweza kuhifadhiwa kwenye cache.
Weka muda mfupi wa kuishi. Kila muunganisho wa SSE unatumia PHP worker. TopVideoHub inazuia (caps) stream katika sekunde 55. Muda unapofika, seva inatuma maoni ya mwisho, inafunga stream, na kivinjari kinajiunganisha tena kiotomatiki. Muunganisho huo mpya utapata PHP worker mpya, kuzuia mchakato mmoja kukaa hapo milele.
Ping to stay alive. Send a comment line—something like : ping—every twenty seconds. Comments in SSE are ignored by the browser’s message handler, but they keep the TCP connection warm. Load balancers and CDNs often drop silent connections after thirty or sixty seconds. A cheap newline saves you from that axe.
Respect the user’s tab. When a visitor minimizes or hides the tab, bail out. Listen for visibilitychange in the browser and call eventSource.close(). The server should also detect a client disconnect and terminate the loop. PHP can check connection_aborted() inside a loop. Do not let ghost connections burn workers for people who left ten minutes ago.
The Hard Ceiling: PHP Workers
SSE in PHP is honest about its limits. Every open SSE connection consumes one PHP worker. If your pool has a hundred workers, you have a hundred streams. Full stop. There is no async workaround while you are inside an Apache or PHP-FPM process model. You can tune pm.max_children, but memory and CPU set the real boundary.
That limit bites fast if you are also using workers for regular page loads, API calls, and asset generation. Monitor your worker saturation carefully. If your SSE endpoint starts queuing because all workers are locked in twenty-minute streams, your entire site slows down.
When the numbers no longer fit, move. Go is the usual next step, though Rust, Node.js, or Erlang can play the same role. Go’s goroutines are the key. A goroutine costs a few kilobytes. You can hold tens of thousands of streams on modest hardware without breaking a sweat. The core logic stays identical—read from cache, write to socket—but the runtime switches from heavy processes to lightweight threads.
Do not start there, though. PHP gets you surprisingly far. Validate the product first. When the metrics page shows worker exhaustion instead of database overload, you have outgrown the stack. That is a good problem.
Takeaway
Live counters are not about raw technology. They are about protecting your database from your own users. Start with SSE because it is simpler than it looks. Cache aggressively between the stream and the database so connection count does not become query count. Watch your worker limits like a hawk. And start simple. PHP is enough until it is not, and by then you will know exactly why you are rewriting.
For your next project:
- Use SSE when the data flows one way, from server to browser.
- Put a cache layer in front of the database. One query every few seconds beats thousands.
- Cap SSE connections to under a minute and let the browser reconnect.
- Close streams on tab hide. Do not fund idle connections with live workers.
- Monitor PHP worker usage. When you max out, port the streaming layer to Go.
