1. 程式人生 > 實用技巧 >go語言程式設計之旅筆記4

go語言程式設計之旅筆記4

第四章: websocket服務

  1. 簡介

    我寫筆記的目的是為了記錄所用到的一些元件,但是從這章開始到結束元件不像前面多了。

    ws可在單個tcp連線上建立全雙工通訊,允許服務端主動向客戶端輸出。保持連線狀態,是一種有狀態的應用層協議。建立方式通過可以http代理來握手,使用HTTP Upgrade頭進行協議升級。

  2. 庫選擇

    書上寫了多種庫,包括nhooyr.io/websocket和gorilla/wesocket等,後者成熟功能多,前者更符合go的習慣並且有併發,完整關閉,可以編譯為wasm等好處。

    go get -u nhooyr.io/websocket
    
  3. 基本用法

    server.go
    package main
    
    import (
    	"context"
    	"fmt"
    	"log"
    	"net/http"
    	"time"
    
    	"nhooyr.io/websocket"
    	"nhooyr.io/websocket/wsjson"
    )
    
    func main() {
    	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    		fmt.Fprintln(w, "HTTP Hello")
    	})
    
    	http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
    		conn, err := websocket.Accept(w, r, nil)
    		if err != nil {
    			log.Println(err)
    			return
    		}
    		defer conn.Close(websocket.StatusInternalError, "internal error")
    		ctx, cancel := context.WithTimeout(r.Context(), time.Second*10)
    		defer cancel()
    
    		var v interface{}
    		err = wsjson.Read(ctx, conn, &v)
    		if err != nil {
    			log.Println(err)
    			return
    		}
    		log.Printf("client resvered: %v\n", v)
    
    		err = wsjson.Write(ctx, conn, "Hello websocket client.")
    		if err != nil {
    			log.Println(err)
    			return
    		}
    
    		conn.Close(websocket.StatusNormalClosure, "")
    	})
    
    	log.Fatal(http.ListenAndServe(":2021", nil))
    }
    
    
    client.go
    package main
    
    import (
    	"context"
    	"fmt"
    	"time"
    
    	"nhooyr.io/websocket"
    	"nhooyr.io/websocket/wsjson"
    )
    
    func main() {
    	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
    	defer cancel()
    
    	c, _, err := websocket.Dial(ctx, "ws://localhost:2021/ws", nil)
    	if err != nil {
    		panic(err)
    	}
    	defer c.Close(websocket.StatusInternalError, "internal error")
    	err = wsjson.Write(ctx, c, "Hello websocket server")
    	if err != nil {
    		panic(err)
    	}
    
    	var v interface{}
    	err = wsjson.Read(ctx, c, &v)
    	if err != nil {
    		panic(err)
    	}
    	fmt.Printf("resvered server's response : %v\n", v)
    	c.Close(websocket.StatusNormalClosure, "")
    }
    
    
    
  4. 其他

    github和部落格園都支援不了md的圖

    sequenceDiagram
    client->>home/: HTTP / 請求首頁
    home/->>client: 聊天室頁面
    client->>webscoket: 通過ws協議請求: /ws
    webscoket->>webscoket: 協議轉換
    webscoket->>client: 響應
    webscoket->broadcast: channel通訊
    webscoket->client: 通過ws長連線傳送訊息
    

    程式碼沒有特別的,不仔細貼了,基本就是channel的操作,完整的還是去github。

    一段單例的程式碼,雙檢鎖基本都長這樣,不過沒有看到‘易變’。golang有沒有這個東西我不記得了
    package singleton
    
    import "sync"
    
    type singleton2 struct {
    	count int
    }
    
    var (
    	instance2 *singleton2
    	mutex     sync.Mutex
    )
    
    func New() *singleton2 {
    	if instance2 == nil {
    		mutex.Lock()
    		if instance2 == nil {
    			instance2 = new(singleton2)
    		}
    		mutex.Unlock()
    	}
    	return instance2
    }
    func (s *singleton2) Add() int {
    	s.count++
    	return s.count
    }
    
    

    在敏感詞處理環節介紹了DFA和貝葉斯分類演算法,提到了兩個庫

    go get -u github.com/antlinker/go-dirtytilter
    go get -u github.com/jbrukh/bayesian
    

    使用者識別用的token生成方式和之前章節一樣,離線訊息處理使用了內建的container/ring環形連結串列