簡單說說net.rpc

Overview
net.RPC並非一個複雜的包,而且其示例代碼已經將基本用法展現得很是清楚。可是在閱讀RPC的文檔,對其全部的功能一一探究時卻產生了很多疑惑。好比,ServeCodec, ServeRequest, ServeConn的區別等。單看文檔是沒法把這些疑惑搞清楚的,只有深刻代碼才能弄明白它們之間的區別。
 
ServeCodec和ServeConn
 
先看一下兩個函數的聲明:
func ServeConn(conn io.ReadWriteCloser)
func ServeCodec(codec ServerCodec)
從調用關係來看ServeConn最終會調用ServeCodec來解析從客戶端發來的調用請求。這一點其實很容易理解,必須先要創建鏈接,而後纔會有數據用來解析。但這兩個函數放在一塊兒,單看文檔的話着實讓人困惑,由於這兩個函數的做用是徹底同樣的:都是用來接收和處理客戶端的調用請求的。
寫慣了C++和看慣了C#的代碼,從個人正常思惟來理解的話,應該有一個相似SetCodec這樣的函數。這樣用戶能夠調用SetCodec來設置codec,而後再調用ServeConn來處理調用請求。在ServeConn裏面先從緩衝區裏面讀取數據,而後傳給codec作解析,再根據codec的返回結果決定要調用哪一個註冊service,最後把調用結果返回給客戶端。
然而現實是卻只有一個奇怪的ServeCodec。既然ServeCodec與ServeConn具備相同的做用,那麼ServeCodec就不僅是簡單地設置一個codec而已,它應該包括一個完整的處理流程。問題是ServeCodec的數據來自哪裏呢?從函數聲明上能夠看出ServeConn的數據確定來自於它的傳入參數,難道ServeCodec的數據也來自於它的參數?
先來看一下ServerCodec的聲明:
<type ServerCodec interface {
    ReadRequestHeader(*Request) error
    ReadRequestBody(interface{}) error
    WriteResponse(*Response, interface{}) error
    Close() error
}

從ServerCodec的接口聲明上能夠看出,一個ServerCodec已經包含了咱們但願一個connection所能完成的全部事情。ReadRequestHeader讀取報頭,ReadRequestBody讀取內容,而RPC的調用結果則經過WriteResponse返回給調用者。也就是說ServerCodec的實現者不只要傳來的數據解析出來,還要負責從connection中讀寫數據。net/rpc默認採用encoding/gob編解碼數據,src/pkg/net/rpc/server.go文件中gobServerCodec實現了這些功能:
type gobServerCodec struct {
rwc    io.ReadWriteCloser
dec    *gob.Decoder
enc    *gob.Encoder
encBuf *bufio.Writer
}
gobServerCodec封裝了gob編解碼器,還有一個ReadWriteCloser接口。注意到ServeConn的傳入參數也是ReadWriteClose類型的,ServeConn函數的實現完美演示了gobServerCodec的用法:
func (server *Server) ServeConn(conn io.ReadWriteCloser) {
buf := bufio.NewWriter(conn)
srv := &gobServerCodec{conn, gob.NewDecoder(conn), gob.NewEncoder(buf), buf}
server.ServeCodec(srv)
}
咱們再貼出Conn的部分聲明:
type Conn interface {
    Read(b []byte) (n int, err error)
    Write(b []byte) (n int, err error)
    Close() error
    ......
}
能夠看到Conn實現了io.ReadWriteCloser接口。也就是說用戶將connection傳給了ServeConn,而ServeConn則把connection又傳給了gobServerCodec。至此豁然開朗,ServerCodec的實現必須包含對connection的封裝,所有的關鍵點就在gobServerCodec的使用方式上。
 
ServeRequest:
ServeCodec裏面是個死循環,一直不停地從客戶端讀數據。若是沒有數據就掛在那裏。ServeRequest則只讀一次,而後把鏈接關閉。
 
ServeHTTP:
ServeHTTP實際上是個回調,實現了http.Handler接口。當調用HandleHTTP的時候,會把ServeHTTP註冊給http包。有數據來的時候,ServerHTTP就會被http服務器調用。這個接口不須要用戶直接調用,而是要和HandleHTTP配合來使用。還有一點要注意,要用http.Serve()啓動http服務器來監聽用戶請求。
 
總結:
RPC裏面的實現比較混亂,各類功能雜揉在一塊兒,想要把這些接口都弄清楚,必須深刻到源代碼裏面。幸好GO語言首選的分發方式爲源代碼方式,不然僅憑RPC的文檔水平是不能很好地支持開發的。Duck-typing確實很靈活,靈活到會有摸不着頭腦的狀況出現。若不熟悉net.http很容易就被ServeHTTP搞迷糊了。ServeConn和ServeCodec的區別也不是那麼清晰和明白。
相關文章
相關標籤/搜索