iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 24
1
自我挑戰組

Let's Eat GO ! 實務開發雜談by Golang系列 第 24

Day24 .[心得與討論篇] struct 設計解析 - 以melody package (4)

今天我們繼續來看Melody,hub,Session三個業務主體,它們struct當中值得關心的部分。

用前三篇的分析,可以充分了解到,Melody,hub,Session是『1』對『1』對『多』的關係。

而每個Session,又有個變數記著它們最上面的Melody的關係。

// Session wrapper around websocket connections.
type Session struct {
	Request *http.Request
	Keys    map[string]interface{}
	conn    *websocket.Conn
	output  chan *envelope
	melody  *Melody
	open    bool
	rwmutex *sync.RWMutex
}

type hub struct {
	sessions   map[*Session]bool
	broadcast  chan *envelope
	register   chan *Session
	unregister chan *Session
	exit       chan *envelope
	open       bool
	rwmutex    *sync.RWMutex
}

type Melody struct {
	Config                   *Config
	Upgrader                 *websocket.Upgrader
	messageHandler           handleMessageFunc
	messageHandlerBinary     handleMessageFunc
	messageSentHandler       handleMessageFunc
	messageSentHandlerBinary handleMessageFunc
	errorHandler             handleErrorFunc
	closeHandler             handleCloseFunc
	connectHandler           handleSessionFunc
	disconnectHandler        handleSessionFunc
	pongHandler              handleSessionFunc
	hub                      *hub
}

輔以package裡面的source code(到了要看code的時候喔),筆者來逐一解釋:

Melody

首先說的是內部許多function型態變數的部分,後續會有一篇專門介紹event trigger,打算專門介紹這部分,如此的設計可說是非常非常好用,細節暫且之後再介紹。

不過還是有個重點,綁定function的變數放在Melody,而不是放在hub或Session,則代表著是Melody這個業務會負責提供package外面的使用事宜,提供外面怎麼樣的使用方式,是Melody來決定的。

messageHandler           handleMessageFunc
messageHandlerBinary     handleMessageFunc
messageSentHandler       handleMessageFunc
messageSentHandlerBinary handleMessageFunc
errorHandler             handleErrorFunc
closeHandler             handleCloseFunc
connectHandler           handleSessionFunc
disconnectHandler        handleSessionFunc
pongHandler              handleSessionFunc

對外部接洽業務處理的struct通常會有個大寫的初始化method,不一定要叫New(),或者會有個屬於這個package的全域變數,在這個package第一次被import的時候,自動初始化一個預設的struct給外面使用的概念。

func New() *Melody {
	upgrader := &websocket.Upgrader{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
		CheckOrigin:     func(r *http.Request) bool { return true },
	}

	hub := newHub()

	go hub.run()

	return &Melody{
		Config:                   newConfig(),
		Upgrader:                 upgrader,
		messageHandler:           func(*Session, []byte) {},
		messageHandlerBinary:     func(*Session, []byte) {},
		messageSentHandler:       func(*Session, []byte) {},
		messageSentHandlerBinary: func(*Session, []byte) {},
		errorHandler:             func(*Session, error) {},
		closeHandler:             nil,
		connectHandler:           func(*Session) {},
		disconnectHandler:        func(*Session) {},
		pongHandler:              func(*Session) {},
		hub:                      hub,
	}
}

hub

hub 一眼就看到許多channel型態的變數,看到這樣子的設計,要馬上想到什麼呢?

答案是這個package應該會開出許多goroutine,而hub會負責處理這些goroutine的某些業務,最不濟也是充當中繼者的角色。

尤其是看到sessions和rwmutex兩種變數,如果沒有多個gorutine同時操作同一個map,何必須要讀寫鎖去控制呢,或者你會覺得這個讀寫鎖型態的變數,是拿來怎麼做使用的?

	sessions   map[*Session]bool
	rwmutex    *sync.RWMutex

如果還不知道為什麼看到channel,就要聯想到多個goroutine的朋友們,筆者在這邊做個小小的提醒,channel的收和送是不可能在同一個goroutine內完成的,往channel塞東西,沒有從channel取出的話,程式永遠卡在那邊,既然卡在送的動作,又怎麼可能會執行到收的動作呢?

或許你會想說buffer channel的話就不會馬上卡住了,但是這樣設計話......老實說很怪啦。

回過頭來,channel的變數命名,也很常拿來代表它們的業務處理動作,所以在這裡就可以知道hub有這4項主要的業務處理,分別是broadcast、register、unregister、exit。

broadcast  chan *envelope
register   chan *Session
unregister chan *Session
exit       chan *envelope

Session

Session 反而沒什麼好說的,好幾個指標的型態,換句話說就是別人的業務主體,然後session有它們的聯絡方式,有跟那些業務主體有關聯,不過除了*Melody以外,都是別的package的事情。

變數output可能會稍為讓人有點在意,因為hub有個channel變數也是同樣的型態,所以這個業務處理,可以推測是需要Session和hub要一起配合才處理得來。

output  chan *envelope

以上觀察melody的業務主體struct,從正面來看差不多分析完了,簡明的三個struct就可以告訴要使用package的開發者,非常非常多的資訊,大大寫出來的東西就是不一樣,下一篇筆者幫大家挑選ㄧ些內部的method,我們試著用這些method來理解struct的業務處理設計。


上一篇
Day23 .[心得與討論篇] struct 設計解析 - 以melody package (3)
下一篇
Day25 .[心得與討論篇] struct 設計解析 - 以melody package (5)
系列文
Let's Eat GO ! 實務開發雜談by Golang30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言