Angular JS + GO: Маленькие сокеты или делим задачу на потоки

Каждая задача нуждается в оптимальном решении. Я все еще очень люблю PHP за его уникальное сочетание легкости и правильности в правильных руках, но многопоточность — не его конек, это следует признать. А если мы хотим на самом деле много пользователей и онлайн ресурс — то стоит посмотреть в сторону чего-то более предназначенного для потоков. Например, GO

Серверная часть — маленькая радиостанция новостей на GO

main.go

package main
import (
   "log"
   "fmt"
   "os"
   "net/http"
   "database/sql"
   _ "github.com/ziutek/mymysql/godrv"
)
const port   = "наш порт"
const dbName = "наша база"
const dbUser = "наш юзер"
const dbPass = "наш пароль"
const initNewsCount = 10
var DB *sql.DB
func main() {
   var err error
   // DB connection:
   DB, err = sql.Open("mymysql", dbName+"/"+dbUser+"/"+dbPass)
   if err != nil {
      log.Fatal(err)
   }
   defer DB.Close()
   // Web sockets:
   f := newExampleFeed()
   http.Handle("/hook/news",           NewNewsSender(f))
   http.Handle("/ws", f)
   go f.Run()
   // мы же хотим знать кто висит на нашей замечательной станции вестей
   http.HandleFunc("/online", func(w http.ResponseWriter, r *http.Request) {
      f.GetOnline <- true
      fmt.Fprint(w, <- f.Online)
   })
   // поехали!
   log.Print("Listening at port " + port)
   if err := http.ListenAndServe(":" + port, nil); err != nil {
      log.Fatal("ListenAndServe:", err)
   }
}
func AddHeaders(h http.HandlerFunc) http.HandlerFunc {
   return func(w http.ResponseWriter, r *http.Request) {
      w.Header().Set("Access-Control-Allow-Origin", "*")
      w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT")
      w.Header().Set("Access-Control-Allow-Headers", r.Header.Get("Access-Control-Request-Headers"))
      w.Header().Set("Content-Type", "application/json")
      h(w, r)
   }
}

exampleFeed.go

package main
import (
   "net/http"
   "log"
)
type exampleFeed struct {
   *Feed
}
func newExampleFeed() *exampleFeed {
   f := &exampleFeed{Feed: NewFeed(false)}
   f.InitSend = exampleInitSend
   return f
}
func exampleInitSend(send chan []byte, r *http.Request) {
   log.Print("Initial send")
   InitNewsSend(send, r)
}

news.go

Собственно сами новости

package main
 
import (
	"net/http"
	"encoding/json"
	"log"
	"database/sql"
	"time"
	"sync"
	"strconv"
)
 
type News struct {
	ID          int    `json:"id"`
	Title       string `json:"title"`
	Description string `json:"description"`
}
 
type NewsSender struct {
	feed *exampleFeed
	LastSendTime      time.Time
	LastSendTimeMutex sync.Mutex
}
 
func NewNewsSender(f *exampleFeed) *NewsSender {
	return &NewsSender{feed: f, LastSendTime: time.Now()}
}
 
// Implementing http.Handler interface in order to use Feed as http handler:
func (n *NewsSender) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	n.LastSendTimeMutex.Lock()
	lastSendTime := n.LastSendTime
	n.LastSendTimeMutex.Unlock()
	rows, err := DB.Query(`
		SELECT id, title, description, crtime FROM news
		ORDER BY crtime DESC`, lastSendTime)
	if err != nil {
		log.Print(err)
		return
	}
	news := make([]News, 0)
	var img, url sql.NullString
	var t time.Time
	for rows.Next() {
		var n News
		rows.Scan(&n.ID, &n.Title, &n.Description, &t)
		n.Time = t.Unix()
		if t.After(lastSendTime) {
			lastSendTime = t
		}
		news = append(news, n)
	}
	js, _ := json.Marshal(news)
	n.feed.Forward <- js
	n.LastSendTimeMutex.Lock()
	n.LastSendTime = lastSendTime
	n.LastSendTimeMutex.Unlock()
}
 
func InitNewsSend(ch chan []byte, r *http.Request) {
	count, _ := strconv.Atoi(r.FormValue("qty"))
	if count == 0 {
		count = initNewsCount // default
	}
	rows, err := DB.Query(`
		SELECT id, title, description, crtime FROM news
		ORDER BY crtime DESC
		LIMIT ?`, count)
	if err != nil {
		log.Print(err)
		return
	}
	news := make([]News, 0, count)
	var img, url sql.NullString
	var t time.Time
	for rows.Next() {
		var n News
		rows.Scan(&n.ID, &n.Title, &n.Description, &t)
		n.Time = t.Unix()
		news = append(news, n)
	}
	js, _ := json.Marshal(map[string][]News{"news": news})
	ch <- js
}

Клиентская часть вывода новостей на Angular

devApp.factory('wsService', function($rootScope){
	return new SocketService($rootScope, 'ws://our_small_go_server:our_port/ws');
});
function SocketService($rootScope, url, settings)
{
	this.url  = url;
	this.data = {};
	var _this = this;
 
	try {
		this.connection = window['MozWebSocket'] ? new MozWebSocket(url) : new WebSocket(url);
		this.connection.onopen = function(){
			console.log('WS connection is now open.');
		};
		this.connection.onerror = function (error) {
			console.error('There was an un-identified Web Socket error');
		};
		this.connection.onmessage = function (response) {
			if (typeof response.data !== 'undefined') {
				var keysUpdated = {};
				angular.forEach(JSON.parse(response.data), function (values, data_type) {
					if (typeof values === 'undefined') return true;
					var isReplace = typeof settings !== 'undefined' && typeof settings[data_type] !== 'undefined' && typeof settings[data_type].replace !== 'undefined' && settings[data_type].replace;
					if (typeof _this.data[data_type] === 'undefined' || isReplace) _this.data[data_type] = [];
					keysUpdated[data_type] = true;
					angular.forEach(values, function (value) {
						_this.data[data_type].push(value);
					});
				});
				angular.forEach(keysUpdated, function (_, key) {
					$rootScope.$broadcast(key + 'Update.all', _this.data[key]);
				});
			}
		};
		this.connection.onclose = function() {
			console.info('WS connection is now open.');
		}
	} catch (e) {
		console.error('Sorry, the web socket at "%s" is un-available', url);
	}
}

Оставить комментарий

XHTML: Вы можете использовать такие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre lang="" line="" escaped="" cssfile="">