2016/05/08

Swift 2.0 : Хялбар iOS апп хийх

Sunday, May 08, 2016 Posted by Orgilbat.A 1 comment

Swift 2.0 цуврал хичээлийн дараагийн хэсэгт тавтай морилно уу.
Эхний хичээлээр та Swift хэлний үндсэн ойлголтуудыг сурч, гарын мөнгө буюу тип тооцоологч классыг үүсгэж үзсэн билээ.
Харин энэхүү хоёр дахь хичээлээр хялбар iOS апп хэрхэн хийхийг сурах болно. Бүр тодруулбал, тип тооцоологч класстаа юзер интерфэйс (user interface) хийх юм.
Би энэхүү хичээлийг iOS дөнгөж шинээр сурч байгаа эхлэн суралцагч болон туршлагатай хөгжүүлэгч хэн хэнд нь тохиромжтой байхаар бичихийг хичээв.
Энэхүү Swift 2.0 хичээлд зориулж Mac дээр чинь хамгийн сүүлд нийтэд гаргасан (public release) Xcode суулгаастай байх ёстойг анхаарна уу. (Хичээлийг бичиж байх үед Xcode 7.0 хамгийн сүүлийн хувилбар байлаа.) Swift хэл маш хурдтай өөрчлөгдөж байгаа бөгөөд бид шинэ хувилбар гарах бүрт уг хичээлийг засч байхыг амлахгүй ч хичээх болно. Хичээлийн код Xcode -ын хуучин хувилбар, туршилтын хувилбарт ажиллахгүй байж мэднэ.

Эхлэе


Xcode -оо нээгээд File\New\Project гэсэн цэс рүү ор. iOS\Application\Single View Application гэж сонгоод Next дарна.



Product Name гэсэн хэсэгт TipCalculator гэж оруулаад, LanguageSwift -г сонгож, DevicesiPhone -г сонгоно. Use Core Data болон өөр бусад чекбоксууд сонгогдоогүй байх ёстой. Next дарна.



Прожектоо сануулах фолдероо сонгоод Create -г дарна.
Ингэхэд Xcode прожектод шаардлагатай файлуудыг үүсгэсэн байгаа. Xcode -ийн зүүн дээд буланд байгаа iOS Simulator гэсэн сонголтоос iPhone 6S -г сонгож, Play товчин дээр дарж аппаа тестлэж болно.



Хэрэв ямар нэг асуудал гараагүй бол та доорх зурагт үзүүлсэн шиг хоосон цагаан дэлгэцийг харах болно.



Одоогоор хоосон байгаа энэхүү дэлгэцийг бид өөрсдийн шаардлагад нийцүүлэн дүүргэх юм.

Модел үүсгэх нь


Бүх зүйлс дэс дараатай байдагчлан юзер интерфэйс үүсгэхээсээ өмнө аппынхаа моделийг үүсгэх хэрэгтэй. Модел гэдэг бол өгөгдөлтэй, тэрхүү өгөгдөл дээр ажиллах үйлдэлтэй классыг (нэг болон нэлээд хэдэн) хэлдэг.

Энэхүү хичээлээр бидний бүтээх аппын модел нь өмнөх хичээлд үүсгэсэн TipCalculator класс бөгөөд энд нэрийг нь өөрчилж TipCalculatorModel болгоно.

Ингээд энэхүү модел классыг үүсгэхийн тулд : File\New\File рүү ороод iOS\Source\Swift File -г сонгоод Next дээр дарна. Нэрийг TipCalculatorModel.swift гэж өгөөд Create дээр дарна.

Санамж: Плэйграунд файлд байгаа кодыг аппаас дуудаж ашиглах боломжгүй. Плэйграунд бол зөвхөн туршилтын болон прототайп кодонд зориулагдсан тул хэрэв энд бичигдсэн кодыг апп-даа ашиглах бол Swift файл руу шилжүүлэх хэрэгтэй. Яг энд хийж байгаа шиг.

Өмнөх хичээлийн TipCalculator.swift файлыг нээж TipCalculator классыг (зөвхөн классын кодыг, доор байгаа тестийн кодыг оруулахгүйгээр TipCalculatorModel.swift файл руу) хуулж тавиад дараах өөрчлөлтийг хийгээрэй :

1. Классын нэрийг TipCalculatorModel болгож өөрчилнө
2. total болон taxPct -г тогтмолоос хувьсагч болгож өөрчилнө (учир нь аппыг ашиглаж байгаа хэрэглэгч эдгээр утгыг өөрчилж байх юм)
3. Мөн subtotal тогтмолыг компьютэд проперти (computed property) болгоно. Үүний тулд subtotal пропертийг зарласан хэсгийг дараах кодоор солино :

var subtotal: Double {
  get {
    return total / (taxPct + 1)
  }
}

Компьютед проперти нь үнэндээ ямар ч утга агуулдаггүй. Харин бусад утга өөрчлөгдөх бүрт тооцоологдон гардаг. Энд, subtotal -ийн утга total болон taxPct -ийн шинэ утга бүрт харгалзан бодогдож гарна гэсэн үг. 

Санамж: Хэрэв хүсвэл компьютед пропертид сеттер тавьж өгч болно. 
Дараах хэлбэртэй байна :

var subtotal: Double {
  get {
    return total / (taxPct + 1)
  }
  set(newSubtotal) { 
     //... 
  }
}

Таны тавьж өгсөн сеттер өөрийг нь тодорхойлсон бусад илэрхийллийг мөн өөрчилдөг (жишээ нь, total болон taxPct -ийн утга newSubtotal -с хамаарч өөрчлөгдөнө гэсэн үг). Гэхдээ энэ боломж бидний хийж байгаа апп -д ямар ч үүрэггүй тул энэ удаад ашиглахгүй.

4. init хэсэгт байгаа subtotal -г сет хийж байгаа мөрийг устгана
5. Файл дотор байгаа бүх коментийг устгана

Хэрэв дээрх өөрчлөлтийг зөв хийсэн бол файл чинь дараах байдалтай харагдана :

import Foundation
class TipCalculatorModel {
  var total: Double
  var taxPct: Double
  var subtotal: Double {
    get {
      return total / (taxPct + 1)
    }
  }
  init(total: Double, taxPct: Double) {
    self.total = total
    self.taxPct = taxPct
  }
  func calcTipWithTipPct(tipPct: Double) -> Double {
    return subtotal * tipPct
  }
  func returnPossibleTips() -> [Int: Double] {
    let possibleTipsInferred = [0.15, 0.18, 0.20]
    var retval = [Int: Double]()
    for possibleTip in possibleTipsInferred {
      let intPct = Int(possibleTip*100)
      retval[intPct] = calcTipWithTipPct(possibleTip)
    }
    return retval
  }
}

Аппын модел бэлэн боллоо. Одоо үзэгдэх хэсгийг буюу view -ээ хийцгээе!

[үргэлжлэлийг тун удахгүй]

2016/04/07

Сервер-ийн бүтцийг Apache вэб сервертэй

Thursday, April 07, 2016 Posted by Anonymous No comments
    Вэб сервер програм яаж ажилладаг нь ихэнх шинээр суралцаж байгаа хүмүүст ойлгомжгүй, шидэт хайрцаг мэт байдаг байх. Харин интернет-ээс хайхаар ихэвчлэн хэрхэн суулгах, яаж тохируулах талаар олддог болохоос хэрхэн ажилладаг болон ямар бүтэцтэй талаарх мэдээлэл төдийлөн олдоод байдаггүй. Тиймээс өнөөдөр вэб сервер гэж юу болох, хэрхэн ажилладаг талаар танилцуулж бичье гэж бодлоо. Жишээ болгож хамгийн өргөн хэрэглэгддэг Apache web server-ийн ажиллагаа, бүтцийнх нь талаар уншсан судалснаа хуваалцаж байна. Apache web server-ийн талаар үндсэн ойлголтыг авсанаар цаашид интернетээс олж авах мэдээлэлийг ойлгоход илүү дөхөм болно гэж найдаж байна.

Вэб сервер програм гэж юу вэ?

    Вэб сервер програм бол сервер компьютер дээр байрладаг бөгөөд үндсэн үүрэг нь хэрэглэгчээс ирсэн HTTP хүсэлтийг хүлээж авч боловсруулан, хэрэглэгчийн хүсэлтэнд харгалзах хариу HTML болон бусад файл, обьектуудыг буцааж явуулах үүрэг бүхий програм юм. Хэрвээ динамик вэб сервер ажиллаж байгаа бол вэб сервер програм нь тухайн програмын хэлтэй хосолж ажиллах шаардлагатай.

Тиймээс вэб сервер програм нь
-       Интернет протоколууд (http, tcp, ip ftp…) дээр ажилладаг

-       Ирсэн хүсэлтийг задалж боловсруулдаг

-       Бусад сервер програмуудтай (java, php…) хосолж ажилладаг байна

Үүнээс гадна:

-       Тогтвортой ажиллагаа (availability)

-       Уян хатан байх (flexibility)
-       Нэг доор олон хүсэлтийг хүлээж авах (concurrency and isolation)
-       Үйлдлийн системийн ялгааг арилгаж ажиллах (operation system env)
-       Хэрэглээндээ тааруулж тохируулах, функц нэмж хасах (scalability, expandability)
зэрэг боломжуудыг агуулсан байх шаардлагатай.

Apache, Nginx, Microsoft IIS, Webrick гэх мэтчлэн олон төрлийн вэб сервер программууд байдгаас одоогоор Apache веб сервер нийт интернетийн 50-с дээш хувийг хангаж байна.

Apache веб серверийн товч түүх

    1996 оноос Robert McCool үндсийг нь тавьсан веб сервер бөгөөд NCSA HTTPd серверийг цааш нь үргэлжлүүлэн OpenSource болгосон төсөл юм. Одоогоор Apache Software Foundation хөгжүүлэлтийг нь хариуцдаг Unix-д зориулан гаргасан энэхүү вэб сервер нь өнөөдрийг хүртэл хөгжихдөөн олон функцуудыг нэмж улам сайжирсаар өдийг хүрчээ. Apache лиценз бүхий энэхүү програмыг C/C++ болон XML хэл голлон ашиглаж хөгжүүлсэн болно.

Apache-н бүтэц ба хэрэглээ

Apache-г өөрсдийн хэрэглээндээ тааруулж ашиглахад Apache-н ажиллагааны талаарх ерөнхий мэдлэг, бүтэц болон мэргэжлийн хэллэгүүдийг мэдэх шаардлагатай байдаг. Ямар функцууд өөрт нь бэлэн байдгийг мэдсэнээрээ магад зарим хөгжүүлэлтийг веб сайт дээр хийлгүйгээр Apache-н модулиас авч ашиглаж болох юм. Тиймээс дараах ойлголтуудыг ерөнхийд нь тодорхойлъё.
-       MPM – apache Multi-Processing Module
-       APR – Apache Portable Runtime
-       Process болон Thread гэж юу болох, Apache нь эдгээрийг яаж ашигладаг
-       Modular architecture
-       Module

MPM (Multi-Processing Module)

    Apache нь олон зэрэг хүсэлтийг хүлээн авахын тулд параллель ажиллах шаардлагатай болдог ба үүнийгээ гүйцэтгэхийн тулд multi-processing, multi-thread болон mixture буюу thread process-ийн хослолыг ашиглах гэсэн үндсэн сонголтууд байдаг. Үйлдлийн системийн зарим нь thread-тэй сайн ажилладаг бол, зарим нь process-тэй илүү сайн ажилладаг. Энэ сонголтыг хийхэд ашиглаж буй програмын хэл мөн чухал үүрэг гүйцэтгэдэг. Жишээлбэл: PHP нь процесс талдаа сайн ажилладаг бол Java хэл нь аль алинд нь сайн ажилладаг. Энэхүү параллель ажиллагааг Apache-н MPM хэмээх модуль гардан гүйцэтгэдэг.

Multi-processing-н ерөнхий ойлголт

    Компьютер нэг дор олон програмуудыг зэрэг ажиллуулах шаардлагатай ба эдгээр нь кибер ертөнцөд тус тусдаа процесс болон оршдог. Аливаа процесс нь хэд хэдэн resource-тай байдгаас memory resource, cpu resource болон үйлдлийн системийн resource гурав нь голлох үүрэг гүйцэтгэнэ. Memory resource болон cpu resource нь ерөнхийдөө ойлгомжтой ч  үйлдлийн системийн resource нь их өвөрмөц. Нэг ээлжинд ажиллах хугацаа, нээлттэй байгаа файлуудын жагсаалт, хүлээлгэнд байгаа эсэх, сүлжээ ашиглаж байгаа эсэх, процессийн priority (зэрэглэл) гэх мэт нь үйлдлийн системийн resource юм. Мэдээж нэг процессоос нөгөө процессруу шилжих үйлдэл тодорхой хэмжээний хугацаа шаарддаг нь санах ой дахь зарим датаг шинэчлэх, cpu дахь регистерүүдийг шинэчлэх, шилжиж ирсэн процессийн сүүлийн үйлдлийг сэргээх гэх зэргээр хамаарна.
Харин Apache-н хувьд нэг дор олон хэрэглэгчийн хүсэлтийг зохицуулах арга нь шинэ процесс үүсгэх буюу хүсэлт бүрийг нэг процесс хүлээн авч боловсруулах юм. Тийм болохоор хөгжүүлэгчид apache-н тохиргоонд үүсгэж болох процессийн дээд хязгаар тоог зааж өгөх боломжтой. Нөгөөтэйгүүр үйлдлийн систем нь шинээр процесс үүсгэх нь бас их хугацаа шаарддаг учраас тэр болгонд нь үүсгэвэл програм маань удаан ажиллана. Тэгвэл apache энэ асуудлыг process pool буюу процессүүдийг урьдчилан үүсгээд бэлдэх замаар зохицуулах бөгөөд үүнийг мөн тохируулах боломжтой.

Multi-threading

    Multi-threading бол ихэнх хүмүүсийн хэлж заншсанаар хөнгөн процесс гэдэг нь учиртай. Хөнгөрүүлж байгаа гол шалтгаан нь дээр дурьдсан гурван resource-аас (memory, cpu, OS) санах ой нь дундынх байдагт оршино. Нэг процесс олон трэдтэй байх ба бүх трэд адилхан санах ойн датаг хуваалцаж ажиллана. Иймд нэг трэдээс нөгөөд шилжих үйлдэл хурдасна гэсэн үг. Харамсалтай нь үйлдлийн системийн онцлог болон ашиглаж байгаа программын хэлнээс шалтгаалан энэ сонголт боломжгүй болох нь олон ба үүн дээр ашиглаж байгаа хөгжүүлэлтийн сан (library) хүртэл нөлөөлнө.

Mixture буюу процесс трэдийн хослол

    Аль алиныхаа давуу талыг ашиглаж нэгнээр нь нөгөөгийнхөө сул талыг нөхөж байвал процесс трэдийг хослуулах шиг сайхан сонголт байхгүй. Ажиллаж байгаа сервер компьютэртээ тааруулж тохируулбал маш сайн ажиллана.

APR (Apache Portable Runtime


    APR нь Apache-ийн зүгээс MPM-ийн ямар ч горимд ажиллаж байсан вэб сайт хөгжүүлэгчид асуудалгүй ажиллагааг хангах үүрэг бүхий орчин бүрдүүлнэ. APR-ыг Java хэлний JRE (Java Runtime Environment)-тэй адилтгаж болон юм. Өөр өөр үйлдлийн системд суугаад орчины ялгааг арилган адилхан interface-ээр хангана.

Modular architecture

    Apache-ийн бахархал болсон дизайн шийдэл болох modular architecture нь олон функцуудыг өөртөө агуулдаг мөртлөө асуудалгүй ажиллагааг хангаж хурдтай хэвээрээ байдгийн гол нууц гэж хэлж болох юм. Apache-core гэх цөм модуль ба бусад модулиуд гэсэн ерөнхий бүтэцтэй.
    Зөвхөн цөм модуль нь бусад модультай харьцах бөгөөд энэ нь модулиудын хоорондох шууд нөлөөг тусгаарлаж аюулгүй ажиллагааг давхар хангадаг. Мөн хүссэн үедээ шинэ модуль нэмж хасах боломжтой ба ингэж шинээр функц нэмж хасахдаа apache-г унтрааж асаах шаардлагагүйгээрээ Nginx мэтийн серверүүдээс ялгардаг. Modular architecture-ийн хамгийн том давуу тал нь мэдээж scalability бөгөөд маш өргөн сонголт бүхий модулиудаас хүссэнээрээ нэмж хасч хэрэглээндээ тааруулж өөрчлөх боломжтой.
Өргөн хэрэглэгддэг модулиудаас дурьдвал:
-       mod-ssl, mod-security зэрэг аюулгүй ажиллагааны модулиуд
-       mod-perl, mod-python, mod-php зэрэг хөгжүүлэлтийн хэлтэй нийлж ажилладаг модулиуд
-       mod-jk, mod-passenger зэрэг apache серверийг өөр бусад TomCat, Passenger серверүүдтэй хосолж ажиллуулдаг гэх мэтчилэн олон модулиудыг дурьдаж болох юм.
Нэгэнт энэхүү хичээлдээ бүх модулиудыг судалж барахгүйгээс apache цөм модулиа цааш нь дэлгэрүүлье.

Apache Core Module

Apache цөм модуль нь үндсэн таван үүрэгтэй:
-       Хүсэлт хүлээж авах
-       Ирсэн хүсэлтийг зөв задалж харгалзах зөв модульд дамжуулах
-       Модуль хоорондын халилцааг хангах
-       Ирсэн хүсэлт боловсруулалтын ямар шатандаа явж байгааг хянах
-       Бүрэн боловсруулж дууссан хүсэлтийн үр дүнг хэрэглэгчид буцаах

Apache-core-ийн үндсэн бүтцийг зураглавал:

Ажиллагааг үндсэн таван файл голлож хариуцах ба үүрэг хувиарлалт нь:
http_main.c
-       Серверийг асааж унтгаах.
http_protocol.c
-       HTTP хүсэлтийг задалж унших
-       Network буюу сүлжээний холболтыг хангах (TCP, IP, Socket)
-       Хэрэглэгчид хариу датаг буцаах
http_request.c
-       Модулиудад хүсэлт илгээх болон хариуг хүлээж авах
-       Модилиудын ажиллагааны алдаа доголдолыг зохицуулах (exception handling)
http_core.c
-       Apache-ийн үндсэн функц (main function)
-       Бусад сангуудыг зангидаж өгнө
-       Тайлбар комментуудыг агуулна
http_config.c
-       Бүх тохиргоонууд энд байрлана
-       Модуль нэмж хасах ажиллагааг хариуцана.

Apache Request Phase

Apache Request Phase нь хэрэглэгчээс ирсэн хүсэлт нэг бүрийн ямар шат дараалалтай боловсруулагддагийг хянадаг хүсэлтийн төлөв юм. Үүнд:
-        URI to filename translation
 http хүсэлтийн URI хаягийг задлах
-       Header parsing
Хүсэлтийн header (толгой) мэдээллийг задалж унших
-       Access control based on host and more
Хүсэлт ямар файл хүсэлттэй болон ямар модуль ажиллуулахыг тодорхойлох
-       Authenticate: Get user_id from http and validate
Хэрэглэгчийн зэрэглэлийг тодорхойлох (admin, guest…)
-       Authorization
Тодорхойлсон хэрэглэгч ирүүлсэн хүсэлтийг гүйцэтгэх эрхтэй эсэхийг тодорхойлох
-       Determine MIME (return filetypes etc)
Товчхондоо бол модулийг дуудна. Модулиудын онцлогоос шалтгаалж олон үйлдэл хийнэ
-       Fix-ups (update header, replace alias etc.)
Модулиас ирсэн хариуг бас засварлана. Жишээлбэл буцаах хариу response-ийн header-ийг өөрчилнө.
-       Send response to client
Хүсэлтийг ирсэн хаягаар нь буцаана. Амжилтгүй бол дахиж явуулна.
-       Log request
Хүсэлтийг гүйцэтгэлийг баримтжуулж Log болгож үлдээнэ
-       Cleanup
Гүйцэтгэсэн хүсэлтийг биелүүлэхэд шаардагдсан орчинг цэвэрлэж шинэ хүсэлт хүлээж авахад бэлдэнэ.
энэхүү төлвүүд нь apache 1.3 дээр үндэслэсэн болно.

Ажиллагаа ба тохиргооны хувьд

    Дээр дурьдсанчлан apache хүсэлт хурдан хүлээж авахад бэлдээд процесс болон трэдүүдийг урьдчилж үүсгэдэг гэсэн. Тэгвэл apache нэг хүсэлтийг боловсруулахад хэдий хугацаа зарцуулсан, дараагийн хүсэлт хэр удаан хүлээсний эцэст ирсэн зэргийг хөтөлдөг онооны самбартай ба тэрнийхээ үр дүнд үндэслэн одоо байгаа процесс болон трэдийн тоо хангалттай эсэхийг тодорхойлдог аж. Энэ нь ид ачааллын үед нэмж олон процесс, трэд үүсгэх ба ачаалал багатай үед зарим процессээ устгаж ажиллахад нь тус болдог байна. Анхны тохиргоогоороо apache 3-аас 5-н процесс бэлэн байлгадаг, нэг процесс дээд тал нь 50-н трэп үүсгэх боломжтойгоор тохируулагдсан байдаг ба тухайн агшинд 256-н хүсэлтийг хүлээж авч боловсруулж байх чадвартай юм байна.

   Өөр нэгэн чухал хүчин зүйл бол интернет холбол (tcp) юм. Аливаа вэб сайтыг ачааллахад html, css, зурагнууд гэх мэт олон файлын хүсэлтүүд ирдэг бөгөөд тэр болгонд нь шинээр холболт үүсгээд байвал асар удахаас гадна серверт их хэмжээний ачаалал өгнө. Тиймээс apache нэг холболтоор дээд тал нь 100-н хүсэлт хүрээж авахаар тохируулагджээ. Энэ нь мэдээж нэг л хэрэглэгчийн 100 хүсэлт ба энэнээс их хүсэлт ирвэл дундуур нь холболтоо таслаад дахиж шинээр холболт үүсгэнэ. Гэхдээ энэ тоо ихэнхдээ хангалттай юм.

    Төгсгөлд нь гэж хэлэхэд Apache болон вэб серверийг хамарсан сэдэв маш өргөн учраас өнөөдөр зөвхөн багахан хэсгийг нь л авч үзлээ. Бид apache-ийн бүтэц ажиллагааг нарийвчилж харсанаар магад ирээдүйд өөрсдийн ажил амьдралдаа apache-ийн шалгарсан шийдлээс санаа авч хөгжүүлэлтээ хийх, суралцах, наандаж л өөр бусад серверүүдтэй харьцуулж давуу тал сул талыг нь тодорхойлох чадвартай болон юм.

2016/01/29

Node.js, MySQL ашиглан вэб push notification хийх

Friday, January 29, 2016 Posted by Anonymous , , , No comments

Push notification гэж юу вэ?

Push notification бол орчин үеийн вэб, аппликэйшнүүдийн салшгүй нэгэн ойлголт юм. Магадгүй хөгжүүлэгч биш хүмүүс push notification гэж яг юуг хэлээд байгааг ойлгохгүй байж болох юм. Тэгвэл дараах жишээгээр тайлбарлая.

Хэрвээ push notification байхгүй байсан бол шинэ и-мэйл ирсэн эсэх, фэйсбүүкт оруулсан зураг дээрээ хэн лайк дарсан, коммент бичсэн эсэхийг тэр дор нь мэдэх аргагүй болно. Тийм болохоор и-мэйл, фэйсбүүк рүүгээ байн байн орж шалгахаас өөр аргагүйд хүрнэ.
Push notification-ийг хэрэглэгчийн үйлдлээс үл хамааран сервэрээс хэрэглэгчид шууд дамжуулж буй мессеж гэж ойлгож болно.

Аппликэйшн дангаараа хэрэглэгч рүү push notification илгээх боломжгүй. iOS үйлдлийн системтэй бол APNS (Apple Push Notification Service), Android бол GCM (Google Cloud Messaging) гэсэн Push Service-үүд энэ үүргийг гүйцэтгэнэ. Эдгээр сервисийн онцлог нь тухайн аппликэйшнийг ажиллуулаагүй байсан ч notification хэрэглэгчийн төхөөрөмж дээр ирнэ.

Харин энгийн вэб хуудсанд push notification илгээхийн тулд заавал APNS, GCM зэрэг төлбөртэй сервисүүдийг ашиглах шаардлагагүй. Гэхдээ браузер нээлттэй, хэрэглэгч логин хийсэн байх хэрэгтэй. Үүнийг HTML5-ын WebSocket технологийг ашиглан хийж болно. Доорх жишээгээр хийж үзье.

Вэб push notification хийх жишээ

Жишээг Windows үйлдлийн систем дээр бэлдлээ. Бусад үйлдлийн систем дээр ч гэсэн доорх заавраар бэлдэхэд ялгаагүй ажиллана. 

Орчин бэлдэх

  1. Node.js суулгана
  2. MySQL Server суулгана
Node.js-ийг суулгахдаа https://nodejs.org руу ороод хамгийн сүүлийн үеийн LTS хувилбарыг татаж суулгана. 
MySQL cерверийг http://www.mysql.com руу ороод MySQL Community Server -ийг татаж суулгаарай.

Жишээнд ашиглах бааз үүсгэх

MySQL cерверээ суулгасан бол доорх query-г ажиллуулж nodejs гэсэн бааз үүсгэнэ..

CREATE DATABASE  IF NOT EXISTS `nodejs`;
USE `nodejs`;

DROP TABLE IF EXISTS `messages`;
CREATE TABLE `messages` (
  `id` int(10) NOT NULL DEFAULT '0',
  `title` text NOT NULL,
  `text` text NOT NULL,
  `photo` text NOT NULL,
  `read_flg` tinyint(1) unsigned DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Фолдер үүсгэх

Command Prompt нээгээд push_notification гэсэн фолдер үүсгэе.  

mkdir push_notification
cd push_notification

Mysql, Socket.io package-уудыг суулгана.

npm install mysql@2.0.0-alpha9
npm install socket.io

Command Prompt доорх байдалтай харагдана.

Үүсгэсэн фолдер дотроо client.html, server.js гэсэн 2 файлыг доорх байдлаар үүсгэнэ.

client.html файл

<html>
<head>
    <title>Push notification</title>
    <style>
        body {
            font-family: sans-serif;
            font-size: 14px;
            font-weight: 400;
        }
        header {
            background-color: #2F6FAD;
            padding: 8px 10px;
            color: #fff;
        }
        span#notification_number {
            padding: 2px 6px;
            background-color: red;
            color: #fff;
            font-weight: bold;
        }
        dd, dt {
            float: left;
            margin: 0;
            padding: 5px;
            clear: both;
            display: block;
            width: 100%;
        }
        dt {
            background: #ddd;
            font-weight: 700;
        }
        figure {
            float: left;
            width: 120px;
        }
        dd p {
            position: absolute;
            float: right;
            margin-left: 150px;;
        }

    </style>
</head>
<body>
<header>Notification <span id="notification_number"></span></header>
<div id="container">Loading ...</div>
<script src="socket.io/socket.io.js"></script>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>

    // вэбсокет шинээр үүсгэх
    var socket = io.connect('http://localhost:8000');
    // тессэж ирсэн үед харуулах
    socket.on('notification', function (data) {
        $('#notification_number').html(data.messages.length);
        var messageList = "<dl>";
        $.each(data.messages, function (index, msg) {
            if (msg.read_flg==0) {
                messageList += "<dt>" + msg.title + "</dt>\n" +
                        "<dd>" +
                        "<figure> <img width='100px' src='" + msg.photo + "' /></figure>" +
                        "<p>" + msg.text + "</p>" +
                        "</dd>";
            }
        });
        messageList += "</dl>";
        $('#container').html(messageList);
    });
</script>
</body>
</html>

server.js файл

var app = require('http').createServer(handler);
var fs = require('fs');
var io = require('socket.io').listen(app);
var mysql = require('mysql');
var connection = mysql.createConnection({
        host: 'localhost',
        user: 'root',
        password: '',
        database: 'nodejs',
        port: 3306
    });
var connectedClients = [];
var pollingTimer;
const POLLING_INTERVAL = 1000;

// датабэйстэй холбогдох
connection.connect(function (err) {
    if (err) {
        console.log(err);
    }
});

// сервер үүсгэх ( localhost:8000 )
app.listen(8000);

// сервер ажиллаж эхэлсний дараа client.html хуудсыг дуудах
function handler(req, res) {
    fs.readFile('client.html', function (err, data) {
        if (err) {
            console.log(err);
            res.writeHead(500);
            return res.end('client.html хуудыг нээхэд алдаа гарлаа.');
        }
        res.writeHead(200);
        res.end(data);
    });
}

// Хэрэглэгч холбогдсон үед тогтмол интервалын хооронд
// баазаас өгөгдлийг хуудас руу дамжуулна.
var pollingLoop = function () {

    // баазаас өгөгдөл авах
    var query = connection.query('SELECT * FROM messages WHERE read_flg=0');
    var messages = [];

    query
        .on('error', function (err) {
            console.log(err);
            updateSockets(err);
        })
        .on('result', function (msg) {
            messages.push(msg);
        })
        .on('end', function () {
            // хэрвээ холбогдсон хэрэглэгч байвал функцыг өөрийг нь дахин дуудна
            if (connectedClients.length) {
                pollingTimer = setTimeout(pollingLoop, POLLING_INTERVAL);
                updateSockets({
                    messages: messages
                });
            } else {
                console.log('Холбоотой хэрэглэгч байхгүй тул сервер зогслоо.')
            }
        });
};

// шинээр сокет үүсгэх
io.sockets.on('connection', function (socket) {

    console.log('Холбогдсон хэрэглэгчдийн тоо:' + connectedClients.length);

    // дор хаяж нэг хэрэглэгч холбогдсон үед loop-ийг эхлүүлнэ.
    if (!connectedClients.length) {
        pollingLoop();
    }

    socket.on('disconnect', function () {
        var socketIndex = connectedClients.indexOf(socket);
        console.log('socketID = %s холболт тасарлаа.', socketIndex);

        if (~socketIndex) {
            connectedClients.splice(socketIndex, 1);
        }
    });

    console.log('Шинэ хэрэглэгч холбогдлоо!');
    connectedClients.push(socket);

});

var updateSockets = function (data) {
    data.time = new Date();
    console.log('Холбогдсон хэрэглэгчдэд өгөгдөл дамжуулж байна ( холбогдсон хэрэглэгчид = %s ) - %s', connectedClients.length, data.time);
    // сокетоор холбогдсон бүх хэрэглэгчид өгөгдөл дамжуулах
    connectedClients.forEach(function (tmpSocket) {
        tmpSocket.volatile.emit('notification', data);
    });
};

console.log('Браузерийг нээнэ үү. http://localhost:8000');

Энд байгаа MySQL-тэй холбогдох хэсгийг өөрийн локал орчныхоор солино.

var connection = mysql.createConnection({
        host: 'localhost',
        user: 'root',
        password: '',
        database: 'nodejs',
        port: 3306
    });


Дээрх бүгдийг хийсэн бол одоо жишээгээ ажиллуулъя. Command Prompt нээгээд
cd push_notification
node server.js

Жишээ зөв ажилласан бол доорх байдалтай байна.


Браузэр дээр http://localhost:8000 хаягийг нээхэд ингэж харагдана.


Одоо бааз дээрээ өгөгдөл оруулж үзье.
USE `nodejs`;
INSERT INTO `messages` VALUES (1,'Хэрэглэгчдэд ээлтэй хүнсний бүтээгдэхүүн үйлдвэрлэгч шалгарлаа','Нийслэлийн Мэргэжлийн Хяналтын Газраас “Хэрэглэгчдэд ээлтэй хүнсний бүтээгдэхүүн үйлдвэрлэгч” шалгаруулах болзолт уралдааныг хүнс үйлдвэрлэгчдийн дунд зарлаж, АПУ компанийн СҮҮНИЙ ҮЙЛДВЭР шалгарлаа.','http://cdn02.content.ikon.mn/news/2015/11/27/9ceddd_ce314aaa-bb5a-427a-9bc4-aa5005d73fab_x100.jpg',0);
INSERT INTO `messages` VALUES (2,'Японы мэргэжлийн сумод 10 жилийн дараа япон бөх түрүүллээ','Японы мэргэжлийн сумогийн нэгдүгээр сарын башёд озэки Котошогикү түрүүлж сүүлийн 10 жил буюу 58 башёд япон бөх түрүүлээгүй байсан муу үзүүлэлт зогслоо. Япончууд үндэснийхээ спортын оргилд гарч чадахгүй байгаагийн гол шалтгаан нь гарцаагүй монгол бөхчүүдийн амжилттай холбоотой. Японы мэргэжлийн сумо бөхийн дээд зиндаанд сүүлийн 16 жилийн хугацаанд аль орны бөхчүүд ноёлож буйг дүрсжүүлэн харвал 2000-2016 оны хооронд нийт 96 башё буюу барилдаан болоход Монголчууд 71, Япончууд 16, Самао 5, Хавай 2, Болгар, Эстони улсын бөхчүүд тус бүр нэг удаа түрүүлжээ.','http://cdn01.content.ikon.mn/news/2016/1/24/149a0c_Kotoomura_07_x100.jpg',0);
INSERT INTO `messages` VALUES (3,'Улс тунхагласны баярын барилдаанд Ч.Санжаадамба 3 дахиа түрүүллээ','Улс тунхагласны ойн барилдаанд заан Ч.Санжаадамба гурав дахь удаагаа түрүүллээ. Ойр мөд барилдаагүй зааныгаа ирж барилдсанд, тэр тусмаа түрүүлсэнд олон олон дэмжигчид нь их л баяртай байв. Түрүү булаалдах барилдаанд арслан П.Бүрэнтөгс заан Ч.Санжаадамба нар ёстой л хүчээ шавхан, хөлсөө урсган барилдаж, хоёр хоёр удаа барьц сонгон алдсаны дараа П.Бүрэнтөгс барьц сонгон шахаж татаад хойш гишгэдэл алдах хоромд Ч.Санжаадамба түрж хөөн давсан юм. П.Бүрэнтөгс арслан ийнхүү энэ намар, өвлийн барилдаануудаас дөрөв дэх үзүүрээ авлаа. Үүнээс гадна гурав түрүүлснийг бүгд санаж буй биз ээ.','http://cdn03.content.ikon.mn/news/2015/2/18/f280ab_MPA_PHOTO-6134_x100.jpg',0);

Дээрх өгөгдлийг баазад оруулсны дараа браузераа refresh хийгээгүй байхад өгөгдөл маань шууд харагдана.


Үүнийг хэд, хэдэн таб дээр зэрэг нээж туршиж болно. Шинэ таб дээр нээх, хаах, өгөгдөл өөрчлөх тухай бүртээ сервер ажиллуулсан Command Prompt цонх дээр гарч байгаа лог-ийг ажиглаарай.

Одоо бааз дээр байгаа өгөгдлийн read_flg баганы утгыг 1 болгож өөрчилж үзье.

UPDATE `nodejs`.`messages` SET `read_flg`='1' WHERE `id`='1';

Ингэхэд эхний мессеж уншигдсан гэсэн статустай болж браузер дээрээс алга болно.

Сүүлд нь браузер дээр нээсэн бүх  цонхоо хаагаад log-оо харахад cервер Idle горимд шилжинэ.


Төгсгөл

Дээрх жишээнд хэрэглэгчээс браузер дээрээ refresh хийх, ямар нэг AJAX request явуулахгүйгээр зөвхөн бааз дээрх өгөгдлийн өөрчлөлтийг серверээс хэрэглэгчид шууд real time дамжуулж байгааг та анзаарсан байх. Энэ бол Websocket-оор дамжуулж байгаа хэрэг юм.