go websocketテストケース


前回websocketサービスを書き終わった後、最大どれだけの接続に耐えられるかをテストしたいと思って、試してみたらバグが出てきました

  • まとめておきましょう
  • 第1回:パイプを閉じた後もデータを送信し続け、panic
  • を招いた.
    close(ch)
    chdata
    
  • 第2回:いくつかの悪い接続によって送信操作のブロック
  • をもたらす大量のテスト要求が来る.

    一つのプログラムが絶えず情報を送信する
    package main
    
    import (
    	"golang.org/x/net/websocket"
    	"log"
    	"strconv"
    	"sync"
    	"sync/atomic"
    	"time"
    )
    
    var (
    	origin = "https://baidu.com"
    	url    = "wss://*"
    	start  = make(chan struct{})
    	//  
    	done int32
    	//   = msgNums + 1
    	msgNums = 50
    	wg      sync.WaitGroup
    )
    
    func Worker(id int) {
    	ws, err := websocket.Dial(url, "", origin)
    	if err != nil {
    		log.Println("worker ", id, " dial fail:", err)
    		return
    	}
    
    	//  
    	start
    
    	send := 0
    
    	defer func() {
    		ws.Close()
    		log.Printf("worker %3d done, send:%3d 
    "
    , id, send) wg.Done() }() for { msg := []byte("{\"msg\":\"worker " + strconv.Itoa(id) + "\"}") _, err := ws.Write(msg) if err != nil { return } send++ // if send > msgNums { // atomic.AddInt32(&done, 1) return } time.Sleep(time.Second) } } func main() { // for i := range [70][0]int{} { go Worker(i) wg.Add(1) } // close(start) // wg.Wait() // log.Println("done:", done) // , ws, err := websocket.Dial(url, "", origin) if err != nil { panic(err) } for range [5][0]int{} { msg := []byte("end") _, err := ws.Write(msg) if err != nil { panic(err) } time.Sleep(time.Millisecond * 500) } }

    もう1つのプログラムは絶えず情報を受信します(1つの接続しか開かない)
    package main
    
    import (
    	"golang.org/x/net/websocket"
    	"io/ioutil"
    	"log"
    	"sync"
    )
    
    //  ,  err, 
    func Read(ws *websocket.Conn) (data []byte, ok bool) {
    	r, err := ws.NewFrameReader()
    	if err != nil {
    		return
    	}
    	fr, err := ws.HandleFrame(r)
    	if err != nil {
    		return
    	}
    	if fr == nil {
    		return
    	}
    	data, err = ioutil.ReadAll(fr)
    	if err != nil {
    		return
    	}
    	return data, true
    }
    
    var (
    	origin   = "https://baidu.com"
    	url      = "wss://*"
    	m        sync.Map
    	msgNums  = 51
    	readChan = make(chan string, 2000)
    )
    
    func Worker() {
    	ws, err := websocket.Dial(url, "", origin)
    	if err != nil {
    		log.Println("worker dial fail:", err)
    		return
    	}
    
    	exit := make(chan struct{})
    
    	go func() {
    		for {
    			select {
    			case s := readChan:
    				value, ok := m.Load(s)
    				if ok {
    					m.Store(s, value.(int)+1)
    				} else {
    					m.Store(s, 1)
    				}
    			case exit:
    				return
    			}
    		}
    	}()
    
    	for {
    		data, ok := Read(ws)
    		if ok {
    			s := string(data)
    			if s == "end" {
    				close(exit)
    				return
    			}
    			readChan  s
    		}
    	}
    }
    
    func main() {
    	defer func() {
    		num := 0
    		all := 0
    		m.Range(func(key, value interface{}) bool {
    			log.Printf("%20s : %3d", key, value)
    			num++
    			if value == msgNums {
    				all++
    			}
    			return true
    		})
    		//  worker 
    		log.Println("total worker: ", num)
    		//  
    		log.Println("all recv num: ", all)
    	}()
    	Worker()
    }
    
    
  • 最後に、一般的には、接続が確立されさえすれば、送信は問題なく、主に受信能力を考慮する.
  • アリのシングルコアサーバでは、ボトルネックがネットワーク上にあり、カードのデッドエンドであり、接続ごとに毎秒1回送信される場合、70個程度の接続が可能であり、具体的なパラメータ調整後には差があるが、学習や普段の使用としては、100以内の接続数は完全に適任である
  • である