我是一個剛學go語言的菜鳥,尚未資格談論什麼技術分享,只是爲了展現fooking的實際應用,同時把我用go寫的聊天室代碼貼出來供你們消遣,若是有入不了各位法眼的代碼,望輕噴。該聊天室基於fooking,而業務代碼是採用Go + Fastcgi。php
完整的源代碼在 https://github.com/scgywx/fooking/blob/master/example/chat/gateway.go,全代碼200多行,去掉router部分代碼,實際邏輯代碼只有170來行,邏輯簡單,功能強大。html
聊天服務器的入口main函數裏有3個IP和端口配置,分別是Chat服務器、Router服務器和Redis服務器。node
func main() { listener, _ := net.Listen("tcp", "0.0.0.0:9001")//Chat服務器配置 srv := newChatServer("test:9010", "test:6379");//Router配置與Redis配置 fcgi.Serve(listener, srv) }
Chat服務器就是實現主要的聊天邏輯,Router服務器是用於轉發消息,Redis用來存儲用戶信息。上面我講過這個聊天室是基於fooking,因此客戶端不是直接與go通訊,而是透過fooking來訪問的。咱們使用go內置的fastcgi模塊來建立一個服務,而後處理請求便可,這跟http服務器很是像,只是協議規範不一樣而已,看下面的代碼就你知道建立一個fastcgi服務器有多簡單了。python
func (s *ChatServer) ServeHTTP(rep http.ResponseWriter, req *http.Request) { //短鏈接要調用一下這個,不然go不會主動斷開鏈接 //req.ParseForm(); req.Form = make(url.Values); body, e := ioutil.ReadAll(req.Body) if e != nil { fmt.Printf("read request error\n") }else{ sessionid := req.Header.Get("SESSIONID") event := req.Header.Get("EVENT") fmt.Printf("sid=%s, event=%s\n", sessionid, event); //具體的業務邏輯處理 } }
代碼中的sessionid就是當前發送請求的客戶端ID,當咱們要作一些uid與客戶端id映射或者是要發消息給指定用戶的時候就可使用這個ID。event就是當前請求的事件類型(0-表示請求,1-新鏈接,2-關閉鏈接),而聊天室只須要關心客戶端請求和斷開事件。請求多是登錄、發消息或者是加入頻道,而斷開事件咱們就須要把用戶信息刪除,而且把他的退出信息廣播給全部在聊天室的人。代碼以下:
nginx
switch event { case "1"://新鏈接 //TODO case "2"://鏈接關閉 s.logout(sessionid) default://消息處理 if len(body) > 0 { js, err := simplejson.NewJson(body) if err != nil { fmt.Printf("parse JSON error, data=") fmt.Println(body) }else{ r := s.handle(sessionid, js) if len(r) > 0 { rep.Header().Add("Content-Length", strconv.Itoa(len(r))) rep.Write(r) }else{ fmt.Println("no message response") } } } } func (s *ChatServer) handle(sid string, req *simplejson.Json) []byte{ t, _ := req.Get("type").String() switch t { case "login": //登錄 ..... case "join": //加入房間 .... case "msg": //發送消息 ..... default: fmt.Printf("invalid type") } return []byte("") }
在上面的消息處理部分,有兩句rep.Header().Add和rep.Write,這表示若是須要返回數據給當前發請求的客戶端,能夠添加Content-Length頭(表示要返回給客戶端的數據長度),而後調用rep.Write發送數據(相似Http的Request對應一個Response)。git
代碼裏面的消息廣播、用戶分頻道都是在Router部分實現,他是fooking消息轉發與用戶數據維持的中間件。github
其實開發一個聊天室能夠很簡單,能夠直接使用websocket協議,讓客戶端跟服務器直接通訊,簡單方便,通訊代價低。而且作socket服務也不受協議限制,徹底能夠自定義或者是使用其它輕量級的協議,好比mqtt什麼的。然而爲何要使用fastcgi呢?主要是他的協議實現簡單,而且擴展性也很是強,目前世界上最強大的語言php的fpm就是使用該協議與nginx進行通訊,若是你的服務使用fastcgi協議開發,那麼你能夠完美的使用nginx與你的服務進行通訊,這樣作的好處是寫socket服務能像寫web服務同樣調試,開發完一個功能你只須要簡單的執行以下命令便可(固然你可能須要添加少許的代碼來判斷來源是從nginx仍是你本身的客戶端)。
web
curl -d '{"type":"login","name":"xxx"}' 'http://fooking/gateway.php?SESSIONID=aaa&EVENT=0'
網關與邏輯分離另外一個好處是,當業務邏輯代碼須要更新,客戶端毫無察覺的,真正作到無痛更新。另外fastcgi協議自己已經支持多路複用(固然這個須要服務端的支持),這個功能但是大名鼎鼎的http到2.0才支持的喲。redis
協議詳細說明請見:http://www.fastcgi.com/drupal/node/6?q=node/22shell
文章開篇已經說了,我是一個Go語言的初學者,到這裏來不是爲了秀go技,而是爲了展現fooking與各語言的銜接。那麼爲何要使用fooking?我相信不少人都知道網關這個東西(可能在遊戲領域應用的更普遍一些),他其主要的目的是用於承載客戶端的鏈接,把消息轉發與業務邏輯分開,後端開發人員只須要專心寫邏輯便可。這就比如咱們寫web的時候,歷來不須要本身去實現http server。那麼fooking也是這樣一個開源軟件,他將socket服務變的更簡單。更多的特性以下:
1 動態網關添加.
2 每一個客戶端惟一SessionID.
3 組播(相似redis的pub/sub).
4 服務器狀態監控.
5 客戶端事件通知(如:新鏈接、關閉鏈接).
6 後端無語言限制(php, python, go, nodejs, etc...).
7 自定義消息協議.
8 後端長鏈接維持.
fooking的詳細介紹請參見: https://github.com/scgywx/fooking 或者 http://git.oschina.net/scgywx/fooking
第一步(下載和編譯)
git clone https://github.com/scgywx/fooking.git
cd {$FOOKING_PATH}
make
第二步(啓動Router)
cd src
./fooking ../router.lua
第三步(啓動Gateway)
./fooking ../config.lua
第四步(啓動Chat服務器)
go run gateway.go
第五步(測試) 修改example/chat/index.html文件的Websocket的服務器IP和端口(查找ws://)
而後用瀏覽器打開index.html便可