Home

Byphunsanit

load balance

โจทย์คืองานอีเลิร์นนิ่งกระทรวงศึกษามีเซิร์ฟเวอร์ห้าตัวให้ใช้รองรับนักเรียนจากทั้งประเทศ จะกระจายโหลดไปยังเซิร์ฟเวอร์แต่ละตัวแต่ไม่มีโหลดบาลานด์ให้ Moodle สามารถกำหนดไดเรคทอรี่ไว้เก็บข้อมูลได้ก็จะใช้ตัวหนึ่งเป็นตัวเก็บข้อมูลสกอร์มข้อมูลจะได้ตรงกัน และใช้ตัวอื่นๆรัน php หาทางเลือกดูมี 3 วิธีคือ

  1. ให้แต่ละตัวรับไปแต่ละภาค ปัญหาคือเกือบทั้งหมดวิ่งผ่านไอเอสพีไม่กี่ที่ และแยกแทบไม่ได้ว่ามากจากภาคไหน
  2. ใช้ function random สุ่ม server ดูปรากฎว่าการกระจายค่อนข้างกระจุกอยู่ที่ server ตัวหลังๆ เกือบทุกครั้ง
  3. ทำให้ระบบจำได้ว่าจ่ายงานครั้งสุดท้ายไปที่ตัวไหนแล้วขยับไปตัวต่อไปเรื่อยๆ เหมือนเราแจกไพ่ การจะทำให้ php จำค่าล่าสุดได้นั้นมีการรักษาตัวแปรไว้ได้ 4 วิธี
    1. เขียนเป็นไฟล์ อันนี้จะมีปัญหาการเข้าถึงพร้อมกันของแต่ละเทรด(Threads)
    2. Cookie ใช้ไม่ได้เพราะเก็บไว้ที่เครื่องผู้ใช้คนอื่นเข้ามาก็จะอ่านค่าไม่ได้
    3. Session ข้อมูลจะเก็บบนเครื่องเซิร์ฟเวอร์แต่ก็เป็นการเก็บข้อมูลของแต่ละคนเหมือน cookie
    4. เก็บในฐานข้อมูล ใช้ร่วมกันได้ แต่เราต้องการแต่ให้ทำงานได้เร็วที่สุด
      DROP TABLE IF EXISTS `lastserv`;
          CREATE TABLE `lastserv` (
          `used` int(11) default NULL
          ) ENGINE=MEMORY DEFAULT CHARSET=utf8 COMMENT='จำ server ที่ใช้ล่าสุด';
          INSERT INTO `lastserv` VALUES (1);

      จะเห็นคำสั่งแปลกๆ ENGINE=MEMORY คือ MySQL จะมีระบบบริหารดาต้าเบสอยู่หลายตัวให้เลือกใช้ให้เหมาะกับงานต่างๆอ่านเพิ่มเติมได้จาก MySQL Storage Engine Architecture ในบรรดาเอ็นจิ้นของ MySQL จะมีเอ็นจิ้น Memory ซึ่งจะเก็บข้อมูลใน ram ทำให้อัตราการเข้าถึงข้อมูลเร็วที่สุดโดยเราสามาถเลือกได้ตอนสร้างตารางให้ระบุ engine ลงไปด้วย
      ในส่วนโค้ท php ไม่มีอะไรเป็นพิเศษเพียงมีการออพติไมซ์เพิ่มเล็กน้อยคือ

      <?php
      $dsn = mysql_connect('localhost', 'database user', 'databae password');
      mysql_select_db('database name' ,$dsn);
      /* การระบุ datasource จะทำงานได้เร็วขึ้น */
      $sql="SELECT used
      FROM lastserv;";
      $row = mysql_fetch_assoc(mysql_unbuffered_query($sql ,$dsn));
      /* คิวรี่แบบใช้ข้อมูลเพียงครั้งเดียวทิ้งลดการใช้ memory */
      if($row['used'] == 4){
        $next = 1;
      }else{
        $next = $row['used'] + 1;
      }
      $sql="UPDATE lastserv
      SET used= $next LIMIT 1;";
      mysql_unbuffered_query($sql ,$dsn);
      switch($row['used']){
        case '1' : {
          header('Location: http://192.168.1.1');
        }break;
        case '2' : {
          header('Location: http://192.168.1.2');
        }break;
        case '3' : {
          header('Location: http://192.168.1.3');
        }break;
        case '4' : {
          header('Location: http://192.168.1.4');
        }break;
      }