Go net/PRC源碼閱讀client.go

Client端

本篇文章主要是在go net/rpc 的client.go包進行翻譯,並添加註釋之後會對client以及server進行總結,廢話不多說 直接貼代碼了。有不正確的地方還請多多指正。

package rpc

import (
    "bufio"
    "encoding/gob"
    "errors"
    "io"
    "log"
    "net"
    "net/http"
    "sync"
)

// ServerError represents an error that has been returned from
// the remote side of the RPC connection.
// 返回一個遠程連接的錯誤
type ServerError string

func (e ServerError) Error() string {
    return string(e)
}

var ErrShutdown = errors.New("connection is shut down")

// Call represents an active RPC.
type Call struct {
    ServiceMethod string      // The name of the service and method to call.要調用的服務的名稱和方法
    Args          interface{} // The argument to the function (*struct).函數的參數
    Reply         interface{} // The reply from the function (*struct). 函數的返回
    Error         error       // After completion, the error status. 結束之後的錯誤狀態
    Done          chan *Call  // Strobes when call is complete.調用完成時
}

// Client represents an RPC Client. 代表一個RPC客戶端
// There may be multiple outstanding Calls associated
// with a single Client, and a Client may be used by
// multiple goroutines simultaneously.
// 可能會有多個未完成的調用關聯到一個Client,一個Client可能會被多個goroutine一起使用

type Client struct {
    codec ClientCodec

    reqMutex sync.Mutex // protects following 互斥鎖
    request  Request    // server端定義的結構體

    mutex    sync.Mutex // protects following 互斥鎖
    seq      uint64
    pending  map[uint64]*Call
    closing  bool // user has called Close   用戶調用了Close
    shutdown bool // server has told us to stop 用戶告訴我們停止
}

// A ClientCodec implements writing of RPC requests and
// reading of RPC responses for the client side of an RPC session.
// The client calls WriteRequest to write a request to the connection
// and calls ReadResponseHeader and ReadResponseBody in pairs
// to read responses. The client calls Close when finished with the
// connection. ReadResponseBody may be called with a nil
// argument to force the body of the response to be read and then
// discarded.
// 一個clientcodec實現爲一個RPC會話的客戶端RPC請求和RPC響應讀寫。
// 客戶端調用WriteRequest寫請求,調用ReadResponseHeader和ReadResponseBody配合讀響應
// 客戶端在一個鏈接結束後調用Close
// ReadResponseBody也許會被傳入一個nil,迫使響應體被讀取之後被丟棄。
type ClientCodec interface {
    // WriteRequest must be safe for concurrent use by multiple goroutines.
    WriteRequest(*Request, interface{}) error
    ReadResponseHeader(*Response) error
    ReadResponseBody(interface{}) error

    Close() error
}

//發送
func (client *Client) send(call *Call) {
    client.reqMutex.Lock()     // 加鎖
    defer client.reqMutex.Unlock() // defer延遲調用保證會解鎖

    // Register this call. 註冊這次請求
    client.mutex.Lock()
    if client.shutdown || client.closing {   // 若處於停止或關閉狀態 返回
        call.Error = ErrShutdown
        client.mutex.Unlock()
        call.done()
        return
    }
    seq := client.seq
    client.seq++
    client.pending[seq] = call
    client.mutex.Unlock()

    // Encode and send the request.編碼併發送請求
    client.request.Seq = seq
    client.request.ServiceMethod = call.ServiceMethod //要調用的服務名和方法
    err := client.codec.WriteRequest(&client.request, call.Args) //寫入請求
    if err != nil {
        client.mutex.Lock()
        call = client.pending[seq]
        delete(client.pending, seq)
        client.mutex.Unlock()
        if call != nil {
            call.Error = err
            call.done()
        }
    }
}

// 輸入
func (client *Client) input() {
    var err error
    var response Response     // server端定義的結構體
    for err == nil {
        response = Response{} // 賦空值
        err = client.codec.ReadResponseHeader(&response)
        if err != nil {
            break
        }
        seq := response.Seq
        client.mutex.Lock()
        call := client.pending[seq]  // value爲struct call的map
        delete(client.pending, seq)
        client.mutex.Unlock()

        switch {
        case call == nil:
            // We've got no pending call. That usually means that
            // WriteRequest partially failed, and call was already
            // removed; response is a server telling us about an
            // error reading request body. We should still attempt
            // to read error body, but there's no one to give it to.
            // 在請求的期間我們沒有獲取到,這通常意味着WriteRequest部分失敗,請求已經被移除
            // 響應是一個服務告訴我們在讀請求體時的一些錯誤。我們應該嘗試的讀錯誤,但是沒有能獲取的。

            err = client.codec.ReadResponseBody(nil) // 與ReadResponseHeader成對使用並傳入nil迫使丟棄響應體
            if err != nil {
                err = errors.New("reading error body: " + err.Error())
            }
        case response.Error != "":
            // We've got an error response. Give this to the request;
            // any subsequent requests will get the ReadResponseBody
            // error if there is one.
            // 我們得到了一個返回錯誤,把這個返回給請求。如果有錯誤隨後的任何請求都會通過ReadResponse獲取
            call.Error = ServerError(response.Error)
            err = client.codec.ReadResponseBody(nil)
            if err != nil {
                err = errors.New("reading error body: " + err.Error())
            }
            call.done()
        default:
            err = client.codec.ReadResponseBody(call.Reply)
            if err != nil {
                call.Error = errors.New("reading body " + err.Error())
            }
            call.done()
        }
    }
    // Terminate pending calls.
    // 終止請求
    client.reqMutex.Lock()
    client.mutex.Lock()
    client.shutdown = true
    closing := client.closing
    if err == io.EOF {
        if closing {
            err = ErrShutdown
        } else {
            err = io.ErrUnexpectedEOF
        }
    }
    for _, call := range client.pending {
        call.Error = err
        call.done()
    }
    client.mutex.Unlock()
    client.reqMutex.Unlock()
    if debugLog && err != io.EOF && !closing {
        log.Println("rpc: client protocol error:", err)
    }
}

func (call *Call) done() {
    select {
    case call.Done <- call:
        // ok
    default:
        // We don't want to block here. It is the caller's responsibility to make
        // sure the channel has enough buffer space. See comment in Go().
        // 我們不想在這加鎖。這是調用者的責任來確保channel有足夠的內存空間。
        if debugLog {
            log.Println("rpc: discarding Call reply due to insufficient Done chan capacity")
        }
    }
}

// NewClient returns a new Client to handle requests to the
// set of services at the other end of the connection.
// It adds a buffer to the write side of the connection so
// the header and payload are sent as a unit.
// NewClinet返回一個新的客戶端句柄,在鏈接的有效期內鏈接服務端發送請求。
// 它向連接的寫入方添加一個緩衝區,因此請求頭和有效載荷作爲一個整體發送
func NewClient(conn io.ReadWriteCloser) *Client {
    encBuf := bufio.NewWriter(conn)
    client := &gobClientCodec{conn, gob.NewDecoder(conn), gob.NewEncoder(encBuf), encBuf}
    return NewClientWithCodec(client)
}

// NewClientWithCodec is like NewClient but uses the specified
// codec to encode requests and decode responses.
// NewClientWithCodec與NewClient相似,但是它使用指定得編碼器編碼請求,解碼返回
func NewClientWithCodec(codec ClientCodec) *Client {
    client := &Client{
        codec:   codec,
        pending: make(map[uint64]*Call),
    }
    go client.input()
    return client
}

type gobClientCodec struct {
    rwc    io.ReadWriteCloser
    dec    *gob.Decoder
    enc    *gob.Encoder
    encBuf *bufio.Writer
}

// 寫請求
func (c *gobClientCodec) WriteRequest(r *Request, body interface{}) (err error) {
    if err = c.enc.Encode(r); err != nil {  // 編碼
        return
    }
    if err = c.enc.Encode(body); err != nil { // 編碼
        return
    }
    return c.encBuf.Flush() // 將c寫入io
}

func (c *gobClientCodec) ReadResponseHeader(r *Response) error {
    return c.dec.Decode(r)
}

func (c *gobClientCodec) ReadResponseBody(body interface{}) error {
    return c.dec.Decode(body)
}

func (c *gobClientCodec) Close() error {
    return c.rwc.Close()
}

// DialHTTP connects to an HTTP RPC server at the specified network address
// listening on the default HTTP RPC path.
// dialhttp連接到指定的網絡地址http RPC服務器,監聽默認的HTTP RPC路徑
func DialHTTP(network, address string) (*Client, error) {
    return DialHTTPPath(network, address, DefaultRPCPath)
}

// DialHTTPPath connects to an HTTP RPC server
// at the specified network address and path.
// DialHTTPPath連接在指定的網絡地址和路徑HTTP RPC服務器
func DialHTTPPath(network, address, path string) (*Client, error) {
    var err error
    conn, err := net.Dial(network, address)
    if err != nil {
        return nil, err
    }
    io.WriteString(conn, "CONNECT "+path+" HTTP/1.0\n\n")

    // Require successful HTTP response
    // before switching to RPC protocol.
    // 在切換到RPC協議之前需要成功的HTTP響應。
    resp, err := http.ReadResponse(bufio.NewReader(conn), &http.Request{Method: "CONNECT"})
    if err == nil && resp.Status == connected {
        return NewClient(conn), nil
    }
    if err == nil {
        err = errors.New("unexpected HTTP response: " + resp.Status)
    }
    conn.Close()
    return nil, &net.OpError{
        Op:   "dial-http",
        Net:  network + " " + address,
        Addr: nil,
        Err:  err,
    }
}

// Dial connects to an RPC server at the specified network address.
// Dial 鏈接到指定網絡地址的RPC服務
func Dial(network, address string) (*Client, error) {
    conn, err := net.Dial(network, address)
    if err != nil {
        return nil, err
    }
    return NewClient(conn), nil
}

func (client *Client) Close() error {
    client.mutex.Lock()
    if client.closing {
        client.mutex.Unlock()
        return ErrShutdown
    }
    client.closing = true
    client.mutex.Unlock()
    return client.codec.Close()
}

// Go invokes the function asynchronously. It returns the Call structure representing
// the invocation. The done channel will signal when the call is complete by returning
// the same Call object. If done is nil, Go will allocate a new channel.
// If non-nil, done must be buffered or Go will deliberately crash.
// 異步調用函數。它返回call結構體。done的通道將在調用完成時發出相同的調用對象來發出信號。
// 如果done爲nil,Go將會分配一個新的channel。如果non-nil,done必須有緩存否則會崩潰
func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call {
    call := new(Call)
    call.ServiceMethod = serviceMethod //調用的服務的名稱和方法
    call.Args = args  //參數
    call.Reply = reply // 返回
    if done == nil {
        done = make(chan *Call, 10) // buffered.建立有緩存的channel
    } else {
        // If caller passes done != nil, it must arrange that
        // done has enough buffer for the number of simultaneous
        // RPCs that will be using that channel. If the channel
        // is totally unbuffered, it's best not to run at all.
        // 如果調用者傳遞的done不爲nil,他必須保證done具有足夠的緩存,用以保證同時有若干RPCs使用那個channel。
        // 如果channel是無緩衝,就不要運行了
        if cap(done) == 0 {
            log.Panic("rpc: done channel is unbuffered")
        }
    }
    call.Done = done
    client.send(call)
    return call
}

// Call invokes the named function, waits for it to complete, and returns its error status.
// 調用命名函數,等待它完成,並返回其錯誤狀態
func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) error {
    call := <-client.Go(serviceMethod, args, reply, make(chan *Call, 1)).Done
    return call.Error
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章