基於Go的websocketMessage Service

來源:互聯網
上載者:User

標籤:call   func   close   下標越界   model   hash   import   keepalive   version   

  3個月沒寫PHP了,這是我的第一個中小型go的websocket微服務。那麼問題來了,github上那麼多輪子,我為什麼要自己造輪子呢?

  Why 造輪子?

  因為這樣不僅能鍛煉自己的技術能力,而且能協助深入瞭解其中的實現原理。

 

  直接上流程圖:

  

  

 

  其實其中有些痛點並沒有反映出來,比如曆史訊息資料的儲存結構、病發時遇到的一些坑等。

   曆史訊息的儲存結構 :

  即廣播、組播可拆解成單播,那麼代碼就可以變得簡單。

 

  但是,但是,但是,有看到 "ref"? ref表示,使用者的曆史訊息,是否是一個引用, 類似於c/cpp的指標、地址。想一想,如果廣播給1w使用者,那麼是不是要把一個msg push到每一個使用者呢? 

  答案至少有2:

  其一:push msg給everyone,優點:讀取資料時很方便, 缺點:資料大量冗餘,且push一瞬間io量過大,效率低; 

  其二:push msg時,分別儲存:廣播表、組播表、單播表, 優點:分別查詢效能高,無冗餘 , 缺點:綜合查詢使用者的所有曆史訊息時,效能差,而且redis的網路io次數較多,還有時間等排序的問題。

 

  綜合考慮,選用第1種方案。

 

  問題又來了, 這個項目開發順利不,遇到坑沒?

  廢話,技術的活,哪有不帶坑的!

  坑1:panic中斷既出 ,真tmd不是我想要的, 解決方式是:recovery   ( : P

  坑2:環境變數向內包的傳遞,試了幾種辦法不行,最後用一個包作代理,封裝工廠和單例, 最好的解決了。

  

var instance *envfunc NewEnv()*env {env := env{}env.init()env.parameters = make(map[string]interface{})return &env}func SingleEnv()*env{if nil == instance {instance = NewEnv()}return instance}//...

   坑3:websocket跨域問題,解決方案至少有2:可以修改預設設定

// 臨時忽略websocket跨域ws := websocket.Upgrader{}if model.SingleConfig().Http.EnableCrossSite {ws.CheckOrigin = func(r *http.Request) bool { //mock and stubreturn true}}

  或者是在nginx上加這些,相當於在同一個域,推薦用這:

nginx conf:upstream push {ip_hash;server 127.0.0.1:9999 ;keepalive 60;}map $http_upgrade $connection_upgrade {default upgrade;‘‘      close;}server {   listen 80;   server_name dev.push.pub.sina.com.cn;    location /push {        proxy_http_version 1.1;        proxy_redirect off;        proxy_set_header Upgrade $http_upgrade;        proxy_set_header Connection "upgrade";        proxy_set_header Host $host;        proxy_set_header X-Real-IP $remote_addr;        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;        client_max_body_size 10m;        client_body_buffer_size 128k;        proxy_connect_timeout 300;        proxy_send_timeout 300;        proxy_read_timeout 300;        proxy_pass http://push;        fastcgi_keep_conn on;        include        fastcgi_params;    }}

 

  坑4:go map不內建支援並發安全,這是最大的問題。解決稍有點麻煩,需要用到RWMutex鎖。 我參考beego寫的:

package libimport "sync"type RWLocker struct {mtx sync.RWMutex}func NewRWLock()*RWLocker{return &RWLocker{}}func (l *RWLocker)RLockDo(callback func()){l.mtx.RLock()defer l.mtx.RUnlock()callback()}func (l *RWLocker)RWLockDo(callback func()){l.mtx.Lock()defer l.mtx.Unlock()callback()}type Locker struct {mtx sync.Mutex}func NewLock()*Locker{return &Locker{}}func (l *Locker)LockDo(callback func()){l.mtx.Lock()defer l.mtx.Unlock()callback()}type MutexMap struct{m    map[interface{}]interface{}lock *sync.RWMutex}func NewMutexMap() *MutexMap {return &MutexMap{lock: new(sync.RWMutex),m:    make(map[interface{}]interface{}),}}func (m *MutexMap) Size() int{return len(m.m)}func (m *MutexMap) Raw() map[interface{}]interface{} {return m.m}//Get from maps return the k‘s valuefunc (m *MutexMap) Get(k interface{}) interface{} {m.lock.RLock()defer m.lock.RUnlock()if val, ok := m.m[k]; ok {return val}return nil}// Maps the given key and value. Returns false// if the key is already in the map and changes nothing.func (m *MutexMap) Set(k interface{}, v interface{}) bool {m.lock.Lock()defer m.lock.Unlock()if val, ok := m.m[k]; !ok {m.m[k] = v} else if val != v {m.m[k] = v} else {return false}return true}// Returns true if k is exist in the map.func (m *MutexMap) Check(k interface{}) bool {m.lock.RLock()defer m.lock.RUnlock()if _, ok := m.m[k]; !ok {return false}return true}func (m *MutexMap) Keys(ignoreNil  bool, keys ...interface{}) []interface{}{m.lock.RLock()defer m.lock.RUnlock()vals := []interface{}{}for _,k := range keys {if v,ok := m.m[k]; ok {vals = append(vals, v)}else{if !ignoreNil {vals = append(vals, nil)}}}return vals}func (m *MutexMap) Delete(k interface{}) {m.lock.Lock()defer m.lock.Unlock()delete(m.m, k)}

  

 

  基本的坑就是這些了,上線部署當然是jenkins+salt+rsync:

 

 

 

  最後,談下,維護性、調試性。

  首先維護性:目前只遇到幾次go會異常崩潰的情況,一般都是不細心或並發安全沒做好,這個根據日誌、race tool、strace/gdb可以搞定。 

  另外,調試性的話,介於php, cpp之間,和java類似,一般能檢查出問題,並打出日誌,包括數組下標越界等,另外 還有pprof/strace/gdb等神器能用上,還是不錯的。

 

  哈哈,今天就寫這麼多了, 要哄妹子了-----------我閨女。

 

   :P

 

 

 

  

 

 

  

  

 

基於Go的websocketMessage Service

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.