Real Time Web: Next generation website

เคยทวิตไว้นานแล้วครับ เกี่ยวกับเรื่อง Real Time Web เลยขอเก็บมาเขียนบล็อคเดี๋ยวจะกลายเป็นบล็อคอนิเมะไปซะได้ (ที่ BG เป็นรูปอนิเมะเนื่องจากผมต้องการผูก [menome profile](http://menome.sunburn.in.th/whs)​ โปรเจกท์ใหม่ของผม เข้ากับบล็อคครับ สักพักคงจะกลับมาเป็นแบบธรรมดาๆ)

Web 2.0 คงจะได้นิยามเป็น Web+Social ครับ เราผลัดจากการเป็นผู้ผลิตเนื้อหา เป็นผู้ผลิตแพลตฟอร์มสำหรับเนื้อหา ผมเองก็ใช้แนวคิดนี้กับ menome “Social+anime” เรามี Wiki ให้ผู้ใช้สามารถจัดการอัพเดตได้ (แต่ต้องอนุมัติก่อนอยู่ดี) แต่บางจุดพื้นฐานอย่างการเพิ่มอนิเมะก็ต้องมีแอดมินที่ทำหน้าที่ตรงนี้ เพื่อไม่ให้เกิดปัญหาเน่าเสียแบบ foursquare แต่เว็บทุกวันนี้เราก้าวไปด้วย HTML5 แล้ว อะไรละคือยุคถัดไป?

คำตอบของผมคงจะเป็น Real-time web ครับ อาจจะว่า เอ๊ะ มันมีคนใช้หรอ? ที่เราเห็นชัดเจนคงเป็น Twitter นั่นแหละครับ วันแรกที่ผมสัมผัส user stream มันว้าวววมาก (ผมเขียน client เองเลยได้ใช้ก่อนคนอื่นหลายเดือน) จากที่เราต้องรอตอบเมนชั่นสามนาที ห้านาที สิบนาที นั่งกดรีเฟรชรอคำตอบ เราสามารถจะตอบสนองกันได้ทันที ทุกวันนี้เราก็คงเคยชินกับ real-time web ใน notification ของเฟสบุ๊คแล้ว ไม่ต้องนั่งรีเฟรชแค่เปิดเว็บทิ้งไว้ก็ไม่พลาดอัพเดต

ถามว่า เอ๊ะ แล้วเว็บไซต์ทั่วไปจะได้ใช้หรอ? ผมว่าถ้าเว็บที่เป็น 2.0 อยู่แล้วได้ใช้อยู่แล้วครับ การเคลื่อนไหวมันเร็ว น่าจะต้องกระจายออกไปให้ผู้ใช้เร็วขึ้น

หรืออย่างบล็อคผมเนี่ยครับ ก็ใช้ [pubsubhubbub](https://code.google.com/p/pubsubhubbub/) protocol (ลองอ่านชื่อเร็วๆ สิบรอบดู) ถ้า feed reader ใครรองรับก็จะพบว่าพอผมอัพบล็อคจะขึ้นอัพเดตในทันทีเลย ซึ่งใน menome ผมก็ implement pubsubhubbub ไว้ อย่างบล็อคผมนี้เวลาอัพแล้วใน menome ก็จะขึ้นอัพเดตในทันที ทำให้คนอ่านไม่ต้องมานั่งเข้าบล็อคผมทุกวันๆ เพื่อหาข่าว และยังประหยัดเวลา polling ด้วย​ (menome poll blog 30 นาทีครับ)

สำหรับใน HTML5 เองก็มี WebSocket ที่มาตอบโจทย์นี้ครับ เซิร์ฟเวอร์สามารถพุช event เข้ามาหาผู้ใช้ได้ในทันที และแน่นอนคำว่าทันทีคือทันทีจริงๆ ครับ ไม่มี delay จาก polling interval ต่างๆ เลย ใน [live.bd2.in.th](http://live.bd2.in.th) ก็ใช้ websocket ในการอัพเดตข่าว DJ ครับ เมื่อ DJ กด enter แทบจะเรียกได้ว่าวินาทีเดียวกับที่ดีเจได้รับข้อความว่าอัพเดตเรียบร้อยแล้ว ฝั่งคนฟังก็สามารถเห็นข้อความนั้นได้ทันทีแล้วครับ

ปัญหาของ Realtime Web ตอนนี้คือการ implement ไม่ง่ายครับ Apache แสดงอายุมันออกมาแล้วโดยเฉพาะ connection นานๆ แบบนี้ถ้าไม่ใช่ event based คงไม่รอด ฝั่ง nginx เองก็มี plugin ที่ทำงานนี้ครับ (รู้สึกว่าจะไม่ใช่ WebSocket อยู่ดี) แต่ที่มาแรงตอนนี้เลยคือ [nodejs](http://nodejs.org) ครับ ใน bd2.in.th ก็ใช้

แน่นอน nodejs ไม่ใช่ภาษาที่คุ้นเคยแบบ PHP ครับ มันเป็น JavaScript แถมเป็น Async อีก (ลองหา mongodb driver ใน nodejs driver สิครับ แล้วจะรู้ว่า async นรกมาก) ฉะนั้นคนใช้เลยน้อย ของ menome ผมเองยังบอกเลยว่าไม่ทำ websocket ใช้ poll 60 วิแทนเพราะไม่ได้ไหลเร็วขนาดนั้น และก็ขี้เกียจเขียนหลายตัว ขี้เกียจเปิดพอร์ตเพิ่ม

อีกอย่างนึงคือผมเห็นใจคนที่ใช้แชร์โฮสต์ด้วยแหละครับ ในอดีตเรามีฟรีโฮสต์ เอา HTML ไปฝากก็พอ ใครใช้ CMS นั่นกระแดะ ยุคต่อมาใน 2.0 PHP เป็นสิ่งสำคัญที่ขาดไม่ได้ และนั่นคือแชร์โฮสต์ แต่ในยุค 3.0 แบบนี้ nodejs ไม่ใช่จะจัดการง่ายแบบ Apache PHP แล้วครับ คงต้องเพิ่มไปถึงขั้น VPS และ VPS เองก็เป็นปัญหาการจัดการทั้งผู้ดูแล (ที่ต้องเรียนวิธีใช้ลินุกซ์ แถมจ่ายแพงกว่าแชร์โฮสต์อีก) จนไปถึงผู้ให้บริการ (เพราะก็ต้องหาเครื่องมาทำ VPS ที่กินทรัพยากรมากขึ้น) ซึ่งแชร์โฮสต์เองมีมานานแล้ว แต่ VPS นี่ก็เพิ่งได้รับความนิยมครับ

(จริงๆ แล้วโฮสต์ node จริงๆ จังๆ ก็มีเช่น heroku/Joyent แต่พวกนี้ใช้ระบบของตัวเองครับ ใครจะเปิดก็ต้องไปลงทุน R&D มาทำให้ราคามันยังแพง)

Building Blue: bd2.in.th

ผมเป็นคนนึงครับที่ชอบอ่านเรื่องเบื้องหลังเว็บไซต์ต่างๆ มาก ใน Feed reader ผมจะมี Facebook Engineering, Twitter Engineering, Github blog ไว้เลยเพื่อจะอ่านเรื่องพวกนี้ ก็เลยว่าอยากลองมาเขียนเองบ้าง

สำหรับวันนี้โปรเจกท์ที่นำเสนอคือ bd2.in.th ครับ เป็นโปรเจกท์นึงที่ผมภูมิใจมากเพราะระบบมันค่อนข้างจะล้ำสมัยกว่าเว็บไซต์อื่นๆ เลยนะครับ

หน้า bd2.in.th ในปัจจุบัน

ในยุคแรกนั้นมีเว็บไซต์ dek-bd2.com ของ @ntklp ครับ เป็นเว็บประกาศข่าว แล้ว @icedrummer ก็ยืมวิทยุออนไลน์ของผมที่อยู่บนเซิร์ฟเวอร์ผมไปจัดเป็นวิทยุ ซึ่งก็จัดนานๆ ครั้ง ผมเองไม่ได้เกี่ยวกับโครงการนั้นก็ไม่ได้เข้าไปดูครับ ต่อมา @ntklp @ByakkoHD และคนอื่นๆ ก็ได้รวมกันทำเว็บบอร์ดขึ้นมาใหม่ให้เป็นรูปเป็นร่าง โดยตอนนั้นอยู่ที่ dek-bd2.whsgroup.ath.cx และแน่นอนว่ามันเครื่องผม ถึงผมจะไม่ค่อยอยากยุ่งแต่ก็ต้องเข้าไปดู maintain ต่างๆ จนกลายมาเป็น coder นิดๆ หน่อยๆ ครับ จนมาวันนึงเราคิดว่าขี้เกียจเมนเทน hosted sites แล้ว เลยผลักดันให้เว็บต่างๆ ออกไปข้างนอก ก็จะกลายมาเป็น bd2.in.th ครับ (ค่าโดเมนปีแรกสนับสนุนโดย @2DaDream) โดยใช้โฮสต์ clnox ที่ผมซื้อเอาไว้ทำ thaifedora.com มาก่อน (ตอนหลังมีคนรับไปอุปการะครับ แต่ผมเองไม่ค่อยว่างจะไปดูเว็บนั้น) และก็ได้พัฒนามาหลายรุ่นเลยครับ

– dek-bd2.com ใช้ WordPress เว็บบอร์ดผมไม่แน่ใจว่ามีไหม
– dek-bd2.whsgroup.ath.cx ใช้ SMF
– bd2.in.th ใช้ Drupal เป็นหน้าแรก และมี SMF เป็นบอร์ด รุ่นนี้มีธีมหลายตัวเลยครับ ตัวแรกสุดที่ผมทำจะเป็นหน้าเหมือน wordpress.org และตัวต่อมาเป็นตัวสุดท้ายที่มีพื้นเป็นก้อนเมฆ​ (จริงๆ มีรูปเมือง รูปก้อนเมฆ และรูปใต้ดิน) และบอร์ดก็จะใช้ธีมนี้ด้วยครับ​ โดยธีมนี้ได้แรงบันดาลใจจาก f0nt.com

ธีม bd2.in.th บนเซิร์ฟเวอร์ทดสอบ
ธีม FunBD2 บนเซิร์ฟเวอร์ทดสอบ

– ยุคต่อมา @ntklp ได้ตัดสินใจใช้ WordPress ผมเองก็ไม่ค่อยอยากยุ่งกับตรงนั้นแล้ว (ผลจากที่ผมเบื่อที่จะต้องมาทำธีมหลายๆ ที) เลยปล่อยให้ไปทำเอาเอง
– @ntklp ขอผมใช้วิทยุอีกครั้งนึงและได้เปิดเป็น Blue Wave Radio โดยรุ่นแรกใช้หน้าเว็บพื้นๆ เป็น HTML file เดียว ฟังได้อย่างเดียว ไม่มีชื่อเพลงไม่มีอะไรทั้งนั้น
– รุ่นต่อมาของ Blue Wave Radio ผมได้พัฒนาเป็นหน้าเว็บเต็มรูปแบบ รุ่นแรกๆ มีการแสดงชื่อเพลง และมีการดึง cover art โดยใช้ Covershare API

Bluewave รุ่นแรกๆ บนเซิร์ฟเวอร์ทดสอบ (ไอเดียจากหน้า ไก่คุ้ยตุ่ยเขี่ย เว็บ Green Wave)
Bluewave รุ่นสอง ใช้ช่วงปีใหม่ 2554 (ใช้ Blueprint CSS)

– รุ่นต่อมาอีกมีการแชทครับ แรกๆ ใช้ irc.freenode.net ห้อง #dekbd2 ผมได้ปรับ @willwillBot รุ่น IRC ให้มีความสามารถรองรับการขอเพลงต่างๆ โดยอาศัยว่าระบบขอเพลงนั้นยังอยู่บนเครื่องผมเลยทำให้ sync ข้อมูลกันได้โดยง่าย
– ต่อมามันมีคนมาฟลัชประจำ ผมจะแบนก็ไม่ได้เพราะใช้ webchat กัน เลยตัดสินใจเปลี่ยนมาเขียนแชทเองโดย integrate กับ SMF, Facebook, ฐานข้อมูลนักเรียน ใช้เวลาเขียนระบบแชทครึ่งวันครับ! (โมโหมาก) และก็แก้บั๊กมหาศาลอีกวันนึงก็เข้าขั้นพอใช้ได้แล้ว
– นอกจากนี้บลูเวฟยังมีการปรับหน้าตาอีกหลายครั้งครับ รุ่นสุดท้ายก่อนมาใช้โทนมืดในปัจจุบันจะมีรายการเพลงที่ขอให้คนดูดูด้วยครับ

รุ่นนี้ออกมาได้วันเดียวก็เลิกใช้เพราะคนฟังบอกว่ารกมาก

– ต่อมาเปิดเทอม 1/2554 เราได้จัดตั้งเป็นชมรม bd2.in.th (ชื่อชมรมชื่อนี้) ในโรงเรียนครับ มีสมาชิกประมาณ 25 คนในระดับม. 3, 5, 6
– ประชุมชมรมแรกๆ ผมได้เสนอและได้ทำการเปลี่ยนเว็บบอร์ดให้เป็น Vanilla ครับ เนื่องด้วยว่า @ntklp ให้ดูตัวอย่างจากเว็บอื่นๆ เช่นสวนบอร์ดและบดินทรโซน ซึ่งจะเน้นบอร์ดน้อย และมีหน้ารวมกระทู้ โดย Vanilla เองสามารถทำแบบนั้นได้ หรือจะแยกบอร์ดก็ได้
– @ntklp ได้หาเทมเพลทมาให้ผมทำ Blue wave UI ใหม่ และก็ได้มาเป็นบลูเวฟรุ่นในปัจจุบันครับ​ ซึ่งตัดฟีเจอร์ออกไปจำนวนมากเลยเพื่อให้มันครบถ้วนในหน้าเดียว
– ผมได้เขียน Plugin ของ Vanilla ให้มีการทำ notify แบบ Google+ ได้ครับ

มาดูเบื้องหลังเว็บดีกว่าครับ ว่าอะไรอยู่กับอะไร เริ่มแรกที่ส่วนหัวเว็บก่อนนะครับ หัวเว็บ bd2.in.th จะมีแถบ ซึ่งผมเรียกมันว่า Dickbar ตามชื่อของ UIDickbar ใน Twitter for iPhone (แต่ UIDickbar หน้าตาเหมือนแผ่นประกาศเที่ยวบินตามสนามบินนะครับ ไม่ใช่แถบแบบนี้) โดยผมบอกสมาชิกชมรมว่า เว็บไซต์จะใช้ธีมอะไรก็ได้ ใช้ CMS อะไรก็ได้ ไม่จำเป็นต้องเหมือนกันหมด แต่ต้องมีสิ่งหนึ่งบอกว่าเรายังอยู่ในเว็บ bd2.in.th นะ ไม่ได้หลุดไปที่อื่น เลยโค้ดออกมาเป็นตัวนี้ครับ

ตัว dickbar นี้ใช้ WordPress API ครับ หน้าที่จะมี Dickbar ทุกหน้าจะต้องเรียก WordPress เข้าไปก่อนแล้วค่อย include file เข้าไป ซึ่งไฟล์นี้ไฟล์เดียวเรียกปุ๊บจะได้ทั้งแถบเลยครับ โดยประกอบด้วยแถบเมนูที่ดึงมาจาก menu ของ WordPress และ CSS, JavaScript ประกอบ (เมนู drop down ที่เห็นนั่น Pure CSS นะครับ ส่วน JavaScript จะใช้ในส่วนของ unread notify และเดิมจะมีระบบ notify ของบอร์ดด้วยครับ แต่ถูกถอดไปแล้วเพราะแทนด้วยระบบ unread notify นอกจากนี้ยังเคยมี countdown ไปวันสอบ (ไม่มีแล้วเพราะใช้ปฎิทินแทน))

Dickbar

ส่วนต่อมานะครับ คือ unread notify ของบอร์ด ตัวนี้ผมใช้ iframe เข้าไปในบอร์ดครับ โดยผมเขียน module เองเพื่อมาแสดง notify และใช้ parent.window เพื่อบอกให้ JavaScript ของ Dickbar ทราบว่ามี unread เท่าไร แม้ไม่ต้องคลิกก็สามารถรู้ตัวเลขได้ (อันนี้ต้องใช้ทริกนิดหน่อยครับเพื่อให้สามารถข้าม subdomain ได้) สำหรับตัวนี้ ตอนแรกวางแผนจะใช้ WebSocket ครับ แต่เปลี่ยนใช้ให้มัน reload หน้า iframe เอาดีกว่าเพราะข้อความไม่ได้เร่งด่วน

อ้อ สำหรับเว็บบอร์ดนั้นผมลองดึง WordPress เข้าไปแล้วครับ แน่นอนว่าพังครับ ฉะนั้น Dickbar ของบอร์ดนั้นผม copy markup จากหน้าอื่นเข้าไปใส่ในธีมเอาครับ

สำหรับส่วนสุดท้ายนะครับ มีความซับซ้อนที่สุดคือระบบบลูเวฟครับ ผมเคยลองเขียนไดอะแกรมแสดงการทำงานแล้วพบว่าเขียนยากอยู่เหมือนกันเพราะมันซับซ้อนครับ

– เมื่อเปิดเข้าไปหน้า [live.bd2.in.th](http://live.bd2.in.th) นะครับ โปรแกรมจะโหลด WordPress มาแสดง Bar และก็แสดงหน้าให้เรียบร้อย
– จะมีการยิง AJAX request เข้าไปสองที่ครับ ที่แรกเพื่อดูสถานะว่ากำลังเล่นเพลงอะไร และที่ที่สองเพื่อดึงว่าขณะนี้ DJ ตั้งค่าระบบอย่างไร (มีสามค่าครับ คือข้อความประกาศข้างบน การเปิดปิด request และเวลาที่ห้ามขอหลังจากขอไปแล้ว)
– หลังจากนั้นแล้วหากมี DJ จัดปกติ โปรแกรมจะเปิดวิทยุครับ โดยตอนแรกผมใช้ [flowplayer ](http://flowplayer.org) แต่ด้วยข้อจำกัดด้านเนื้อที่เลยมาใช้ [SoundManager](http://www.schillmania.com/projects/soundmanager2/) + [jQuery UI](http://jqueryui.com) แทน
– นอกจากนั้นแล้วโปรแกรมยังจะเปิด​ WebSocket ไปด้วยครับ​​ โดยผมเลือกใช้ [Faye](http://faye.jcoglan.com/) เป็น server ครับเพราะมีลักษณะเป็นห้องๆ และยังสามารถใช้วิธีอื่นๆ ได้หากเครื่องไม่รองรับ WebSocket ด้วยครับ
– ขณะเล่นเพลงอยู่ WebSocket server จะ poll มาหาเพลงที่กำลังเล่นครับ (ไฟล์เดียวกับที่ request ไปตอนเปิดหน้านั่นแหละครับ ผมขี้เกียจ port ลง JavaScript ด้วยปัญหา encoding ต่างๆ) แล้วก็จะปาเข้าไปใน stream ด้วย ทำให้ client ไม่ต้อง poll server เองพร้อมกันหลายๆ คน ส่วน state นั้นเมื่อดีเจอัพเดตข้อมูล โปรแกรมจะส่งเข้ามาใน faye อยู่แล้วครับ ไม่จำเป็นต้อง poll
– Faye สามารถรับส่งได้สองทางครับ เพื่อป้องกันไม่ให้ client ส่งข้อความเข้าไป ผมได้มีการยึนยันด้วย Shared secret ครับ โดยมีรหัสผ่านนึงที่ทั้งสองฝั่งจะทราบและต้องส่งไปด้วย ถ้าไม่มีหรือไม่ถูกแปลว่าปลอมแน่นอนครับ ยกเว้นห้อง user online ผมอนุญาตให้ client ส่งเข้าไปได้เลย แต่จะมี shared secret อีกอันหนึ่งที่จะ sign กำกับข้อมูลที่ส่ง ซึ่ง server generate ให้ ดังนั้นหากมีการเปลี่ยนแปลงข้อมูลไม่ตรงกับที่ server กำหนดมาให้ ถึงจะส่งเข้าไปได้แต่เซิร์ฟเวอร์ก็ไม่รับครับ

สำหรับด้าน backend ของ DJ นะครับ เดิมทีผมจะใช้ htdigest authentication ฉะนั้นดีเจทุกคนจึงมีรหัสเดียวกัน (ผมขี้เกียจครีหลายอัน) ก็จะมีปัญหาตอนจะเอาดีเจออกเพราะมันต้องเปลี่ยนรหัส จนตอนเขียนแชทใหม่ผมเลยได้รื้อ backend ครับ (โค้ดเดิมมันเน่ามากเพราะเขียนแบบ Quick & Dirty ปัจจุบันโค้ดมันก็ก๊อปแปะจากตรงนั้นมาบางส่วนก็ยังเน่าๆ อยู่ครับ) โดยให้ยึนยันตัวด้วย WordPress แทน และใช้ Permission ของ WordPress ไปเลย และ Request ที่ขอมาจะเก็บลง Redis server ครับ และยังจะ push ไปยัง websocket ของ DJ ด้วยนะครับ (DJ มีห้อง websocket ต่างหาก) หลังจากดีเจเปิดเพลงให้แล้วมันจะพุชมาทาง websocket ครับและ client จะหาว่าใช่ของตัวเองมั้ย ถ้าใช่ก็ให้แสดงข้อความว่าเปิดเพลงให้แล้วนะ (ข้อมูลนี้ไม่ใช่ความลับครับ ฉะนั้นจึงให้วิ่งกระจายไปได้เลย) และด้านเซิร์ฟเวอร์ก็จะเซฟลง MySQL เพื่อใช้เป็นข้อมูลในการ Autocomplete ต่อไป

ระบบขอเพลง

ส่วนสุดท้ายในเว็บครับ คือส่วนของออโต้บอทจัดเพลง โดยผมใช้โปรแกรม ices ครับ ที่ผมแพทช์ไว้นิดนึงให้มันไม่ปิดตัวเองเมื่อต่อไม่ได้หลายครั้ง เพราะว่าเมื่อดีเจเตะบอททิ้งแล้วบอตจะหลุด และพอต่อไม่ได้บอตจะปิดตัวเองทำให้เมื่อดีเจจัดเสร็จแล้วบอตไม่กลับมา วิธีนี้ทำให้บอตกลับมาได้เอง โดยไม่ต้องกดเปิดบอตครับ​ (ผมยังคิดวิธีเปิดบอตที่ปลอดภัยไม่ได้เลยนะครับ เพราะบอตมันรันคนละ user กันด้วย) โดย ices อนุญาตให้เขียนสคริปต์ควบคุมได้ครับ ผมใช้ Python เขียนควบคุม โดยมีหลายโค้ดดังนี้ครับ

– ทุกเพลงจะสุ่ม 25% มีโอกาสเปิดจิงเกิ้ล​​ (ตอนหลังผมแก้ไว้ว่าห้ามเปิดจิ้งเกิ้ลต่อเนื่อง เพราะเคยมีบั๊กเปิดต่อกันห้ารอบแล้วนะครับ)
– เวลาแปดโมงเช้า หกโมงเย็น จะเปิดเพลงชาติไทย และเปิดจิ้งเกิ้ลตาม (ID3 Tag ของเพลงชาติและจิ้งเกิ้ลผม hard code ไว้ครับ เพลงอื่นมันจะดึง ID3 เองได้)
– บอตจะไม่เปิดเพลงศิลปินเดิมติดกัน (เพราะเคยมีบอตเปิดเฉลียงซ้ำๆ จนคนฟังเบื่อ)
– ก่อนจะเข้าระบบ playlist บอตจะดึงข้อมูลจาก redis ว่ามีเพลง request ใหม่หรือไม่ ถ้ามีก็จะเปิดให้ พร้อมแสดงชื่อคนขอให้เสร็จสรรพครับ
– การเตะบอตทำที่หน้า DJ Backend โดยมันจะเตะที่ shoutcast ครับ ไม่ได้เตะโพรเซสบอต เมื่อเตะแล้ว​ DJ จึงต้องรีบเข้ามาจัดต่อทันที

สำหรับ caching ในเว็บ ผมไม่แน่ใจว่าใน CMS ผมเซตอะไรบ้างนะครับ (ใช้ W3 Total Cache ด้วยนะครับ บล็อคผมที่ท่านอ่านอยู่นี้ก็ใช้) แต่ว่าสำหรับใน Blue wave เดิมทีไม่มีแคชครับ (มันเคยบั๊กเลยปลด ก่อนหน้านี้เป็น memcached) ปัจจุบันแคชเพลงที่กำลังเปิดใช้ Redis ครับ