Archive for the ‘Web Server’ Category

0153 | ความเชื่อผิดๆ ใน config apache ที่เจอบ่อย

Monday, March 11th, 2013 Posted in Linux, Web Server | No Comments »

เจอบ่อยๆ มาก็ค่อนข้างหงุดหงิดพอควร เรื่องการตั้งค่า “อะไรบางอย่าง” ของ apache ครับ… เวลาไปจูนเครื่องชาวบ้านแล้วเจออะไรแบบนี้ก็จะปวดหัวพอควร

ความจริงมี MySQL ด้วย แต่เขียนไปเขียนมา apache ก็ยาวแล้ว แยกไปเป็นครั้งหน้า (ที่ไม่รู้ว่าจะมาเมื่อไหร่) แล้วกัน 555+

<IfModule prefork.c>
StartServers       8
MinSpareServers    5
MaxSpareServers   20
ServerLimit      2000
MaxClients       2000
MaxRequestsPerChild  4000
</IfModule>

ตัวอื่นไม่ซีเรียส แต่ตัวที่มีปัญหาคือ ServerLimit กับ MaxClients ครับ สองค่านี้ส่วนมากจะไปคู่กันครับ เพราะ ServerLimit คือค่าสูงสุดที่อนุญาตให้ MaxClients ตั้งได้… (แล้วทำไมมึงต้องแยก! อ่าน docs แล้วยังไม่เข้าใจ) แต่เอาเป็นว่าสองค่านี้ควรจะเท่ากันเสมอครับ ด้านล่างเป็นคำอธิบายจากเว็บของ apache เอง

Special care must be taken when using this directive. If ServerLimit is set to a value much higher than necessary, extra, unused shared memory will be allocated. If both ServerLimit and MaxClients are set to values higher than the system can handle, Apache may not start or the system may become unstable.

With the prefork MPM, use this directive only if you need to set MaxClients higher than 256 (default). Do not set the value of this directive any higher than what you might want to set MaxClients to.

ด้วยความที่หลายคนคงเข้าใจไปว่า ค่าสองตัวนี้คือจำนวน uip หรือจำนวน user online บนหน้าเว็บ หรืออะไรก็มิอาจทราบได้ ทำให้ตั้งค่านี้สูงเกินจริง กะว่าเว็บจะรับคนเข้าได้เยอะๆ แต่หารู้ไม่… คุณเข้าใจผิดครับ

เอาล่ะ ทีนี้ถ้าเซตไอ้บ้านี่ไว้เยอะๆ จะเกิดอะไรขึ้น ขออธิบายเป็นข้อๆ ตามนี้ละกันครับ

  • Apache ปกติ จะใช้วิธีแตก process ย่อย 1 process ต่อ 1 connection
  • รูปแบบการทำงานปกติของเว็บคือ request then response and close คือ เมื่อ client ส่งคำร้องไปแล้ว server ควรจะตอบกลับไปให้เร็วที่สุด แล้วก็ตัดการเชื่อมต่อทันทีที่ส่งข้อมูลเสร็จ
  • หมายความว่า ณ เวลาเดียวกัน (simultaneously) ควรจะมี connection ค้างอยู่บน Apache ไม่เยอะ (ถ้าไม่มีการปล่อยให้ download ไฟล์ใหญ่ๆ)
  • ทีนี้ 1 process ของ apache (ที่มี PHP อยู่ด้วย) เนี่ย มันจะกินแรมประมาณ 20 MB เป็นอย่างน้อย แต่ค่าเฉลี่ยที่เจอจริงๆ คือ 40-50 MB
  • เนื่องจาก เราถือว่า apache ควรจะประมวลผลและตอบกลับไปได้เร็วที่สุด สมมติ 1 request ใช้เวลาตอบกลับได้ภายใน 0.2 วินาที เท่ากับว่า 1 process ของ apache สามารถรับ request ได้ วินาทีละ 5 request หมายความว่า ถ้าตั้งไว้ 100 process ก็สามารถรองรับได้ถึง 500 request ต่อวินาที !!
  • ทีนี้ ถ้าตั้งค่านี้สูงมากๆ กรณีที่เกิดมี script ค้างขึ้นมา… แล้วทำให้ apache มี connection (หรือ process) ค้างเป็นจำนวนมาก ปริมาณแรมที่ apache ใช้ก็จะพุ่งพรวดๆ จนเกินกว่าที่เครื่อง server มีให้ ก็จะไปกิน swap แล้วก็ตายอนาถ ล่มทั้งเครื่องในที่สุด แม้แต่ remote ก็ทำไม่ได้
  • การตั้งค่านี้ไว้ให้พอดี / ใกล้เคียงกับปริมาณแรมที่มี โดยเอาค่าเฉลี่ยการใช้แรมต่อ 1 process มาหารปริมาณแรมที่มี (เช่น มีแรม 8000 MB หารด้วยค่า process ละ 50 MB จะได้ค่าที่เหมาะสมคือราวๆ 160 ) จะช่วยลดผลกระทบเวลา server ทำงานหนักได้ดีมาก เพราะเมื่อ connection เต็ม apache ก็ยังกินแรมไม่หมด หรืออาจเกินมานิดหน่อย ทำให้ process อื่นยังทำงานได้ โดยเฉพาะ remote ทำให้เข้าไปแก้ไขปัญหาได้
  • การตั้งค่า maxclients ไว้สูง จึงไม่ได้หมายความว่า server จะรองรับการใช้งานได้เยอะขึ้นแต่อย่างใด … เอวัง

Tags: ,

0148 | Concept ระบบจองตั๋ว

Sunday, February 17th, 2013 Posted in Database, PHP Coding, Web Server | No Comments »

จากกรณีเดี่ยวสิบระบบจองล่มอนาถ… ในหัวเลยลองร่างๆ ระบบ ticketing system ไว้อยู่พอประมาณครับ เงื่อนไขที่ร่างไว้ก็ประมาณนี้

ไม่รู้ทำจริงจะเน่ามั้ยนะ (ฮา) ออกแบบในหัวมาคร่าวๆ ยังไม่ได้ลงรายละเอียดเลยครับ ไม่รู้จะติดปัญหาอะไรยังไงบ้าง

  • เร็ว : แน่นอน ระบบต้องเร็วพอที่จะไม่ต้องเสียเวลารอในแต่ละขั้นตอนมาก
  • ชัวร์ : ล็อกที่นั่งระหว่างทำรายการได้ชัวร์จนกว่าจะ expire // ไม่อนุญาตให้เกิดการทำรายการจองที่นั่งซ้ำ
  • ปล่อยได้ : ที่นั่งที่ล็อกไว้รอทำรายการต้องมีกำหนด expire ถ้าทำรายการไม่เสร็จตามระยะเวลาก็ให้กลับคืนมาให้ระบบ
  • realtime : รายการผังที่นั่ง stream ดูกันสดๆ ได้เลยว่าที่นั่งไหนอยู่ในสถานะไหน (ajax streaming)

ส่วนประกอบของ service ก็จะได้ประมาณนี้

  • WebUI : ส่วนหน้าจอติดต่อ client อาจเป็น rest api หรือ full web เลยก็ได้
  • Payment Gateway Interface : ส่วนคุยกับระบบจ่ายเงิน (บัตรเครดิต, paypal, อื่นๆ)
  • Locking Services : เป็น In-memory service ที่ทำหน้าที่เก็บสถานะที่นั่งไว้ในแรม
  • Database : เอาไว้เก็บตัว transaction และ state จริงๆ ของที่นั่ง

ที่คิดไว้… ฝั่ง WebUI กับ Payment Gateway Interface นี่เขียนภาษาอะไรก็ได้ครับ ไม่ strict ส่วน locking service เป็น nodejs เนื่องจากต้อง listen http สำหรับ streaming ข้อมูลการ lock ที่นั่งส่งไปให้ client ด้วย … ส่วน database อยากใช้อะไรก็ใช้ไปฮะ

  • locking service สามารถรัน 1 zone ต่อ 1 หรือหลายรอบการแสดงได้ (แต่ไม่สามารถรันหลาย zone ต่อ 1 รอบการแสดงได้ เพราะจะทำให้มีโอกาสที่จะ lock ชนกัน) ทันทีที่รันให้โหลดข้อมูลผังที่นั่งและสถานะที่นั่งแต่ละที่จาก database มา map ไว้รอเลย
  • webui จะให้เลือกรอบการแสดง (ข้อมูลนี้ cache ได้ เพราะงั้นเข้าไม่ถึง database) แล้วจะไป fetch ข้อมูลผังที่นั่งจาก locking service พร้อมเปิด ajax streaming connection ทิ้งไว้เพื่อ update ผังที่นั่งสดๆ
  • พอ client คลิกยืนยันที่นั่งที่เลือก webui จะยิงคำสั่ง lock ไปให้ locking service
  • locking service จะ mark ที่นั่งนั้นเป็น pending payment ทันที พร้อมระบุเวลาที่ทำการ mark ด้วย โดยทำการ async save ลง database ด้วย พร้อม broadcast หา client ทุกตัวที่เชื่อมต่ออยู่ให้รู้ว่าที่นั่งนั้นๆ ไม่ว่างแล้ว
  • ถ้า locking service mark ที่นั่งไม่ผ่าน (update ชน หรือมี client ยิงคำสั่งที่นั่งมาชนกันพอดี) ให้แจ้งคนที่มาทีหลังว่าที่นั่งไม่ว่าง แล้วยิงให้ webui refresh ที่นั่งใหม่
  • webui เก็บข้อมูลอื่นๆ ของผู้ซื้อบัตร แล้ว redirect เข้า payment gateway ที่เจ้าตัวเลือก
    • ถ้าเป็น offline payment อย่าง counter service / atm / bill paymetn ก็ให้ยิงคำสั่งไปบอก locking service อีกรอบเพื่อแก้ไขสถานะเป็น offline payment required เพื่อไม่ให้ที่นั่งหลุด
    • ส่วน online payment ก็ทำรายการต่อไปจนจ่ายเงินสำเร็จ ยืนยันยอดเสร็จ webui ก็ update transaction ลง database ไปเลย แล้วยิงคำสั่งบอก locking service ว่าที่นั่งจองเรียบร้อยแล้ว ขึ้นสถานะเป็น complete ได้ หรือถ้า transaction fail ก็ยิงไปบอกให้ unlock ที่นั่ง
  • locking service ต้องทำ timer รอไว้ (setTimeout) สำหรับที่นั่งที่เพิ่ง mark pending payment ว่าภายใน X นาทีถ้ายังไม่เปลี่ยนสถานะให้ unlock ที่นั่ง <– อันนี้ต้องการความสามารถของ node.js เต็มๆ

สรุปแล้ว ทั้งระบบจะมี query database ประมาณนี้

  • ตอน start locking service ครั้งแรก query หาผังที่นั่ง + สถานะที่นั่ง
  • ตอนคนเลือกรอบการแสดงครั้งแรกๆ ที่ยังไม่ติด cache
  • locking service update สถานะที่นั่งเข้าฐานข้อมูล (free, pending payment, offline payment, completed)
  • webui update ข้อมูลลูกค้า
  • webui update ข้อมูล transaction

ต่อ 1 การจองก็จะมี query สามอันหลัง ประมาณ 7-8 query โดยไม่จำเป็นต้องทำ lock ที่ฝั่ง database ให้ระบบค้างเลย

จุดอ่อนใหญ่สุดของระบบนี้อยู่ที่ตัว locking service ที่ถ้ามันล่มก็จะจองตั๋วในรอบนั้นไม่ได้เลย รองลงมาก็การที่ยังต้อง query เข้า database เกือบสิบครั้งต่อ 1 การทำรายการจองซึ่งอาจเกิดคอขวดที่ database ได้ อาจแก้ได้ด้วยการแยก database ข้อมูลลูกค้ากับข้อมูลการจองออกจากกัน (สิ้นคิดมาก 555)