這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
首先編寫thrift檔案(rpcserver.thrift),運行thrift --gen go rpcserver.thrift,產生代碼
namespace go rpcservice RpcService { string SayHi(1: string name); void SayHello(1: string name);}
搭建一個以二進位為傳輸協議的伺服器如下:
type rpcService struct{}func (this *rpcService)SayHi(name string)(r string, err error){ fmt.Println("Hi ", name) r = "Hello "+name err = nil return}func (this *rpcService)SayHello(name string)err error{ fmt.Println("Hello ", name) err = nil return}func StartServer(){ serverTransport, err := thrift.NewTServerSocket("127.0.0.1:8808") if err != nil { fmt.Println("Error!", err) return } handler := &rpcService{} processor := NewRpcServiceProcessor(handler) server := thrift.NewTSimpleServer2(processor, serverTransport) fmt.Println("thrift server in localhost") server.Serve()}
查看自動產生的程式碼recserver.go,我們發現 NewRpcServiceProcessor函數代碼如下:
func NewRpcServiceProcessor(handler RpcService) *RpcServiceProcessor { self4 := &RpcServiceProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} self4.processorMap["SayHi"] = &rpcServiceProcessorSayHi{handler: handler} self4.processorMap["SayHello"] = &rpcServiceProcessorSayHello{handler: handler} return self4}
也就是說,thrift通過key-value儲存了我們實際將要啟動並執行函數,最終通過handler來執行。
這裡就有點像我們使用golang系統中的http包中的ListenAndServer()函數時,提前通過Handfunc來設定好函數路由是一個意思。
再看看Serve()函數是如何?的:
func (p *TSimpleServer) Serve() error { err := p.Listen() if err != nil { return err } p.AcceptLoop() return nil}func (p *TSimpleServer) AcceptLoop() error { for { client, err := p.serverTransport.Accept()
if err != nil{
//.......被我刪掉
} if client != nil { go func() { if err := p.processRequests(client); err != nil { log.Println("error processing request:", err) } }() } }}
Serve()函數負責監聽串連到伺服器上的client,並且通過processRequests()函數來處理client。實際處理過程中,伺服器會擷取client的processor,然後進一步處理client的請求。這部分先暫停一下,我們來分析一下client端的工作原理,之後再回過頭來看看會比較清晰一些
首先我們架設client端如下,並且通過client端來發送一個SayHi的操作:
transport, err := thrift.NewTSocket(net.JoinHostPort("127.0.0.1", "8808"))
if err != nil { //... } protocolFactory := thrift.NewTBinaryProtocolFactoryDefault() client := NewRpcServiceClientFactory(transport, protocolFactory) if err := transport.Open(); err != nil { //... } defer transport.Close()
res, _ := client.SayHi("wordl")
現在問題來了,這個SayHi是如何通知給伺服器的呢?不急,看源碼
在我們調用thrift --gen go XXX命令的時候,thrift已經給我們產生了SayHi過程的代碼,如下:
func (p *RpcServiceClient) SayHi(name string) (r string, err error) { if err = p.sendSayHi(name); err != nil { return } return p.recvSayHi()}
其中RpcServiceClient類型就是我們的client,可以看到先調用了一個sendSayHi,如果沒有錯誤的話,又調用了一個recvSayHi。
其實sendSayHi就是我們通知伺服器執行SayHi()函數的關鍵,而recvSayHi是接受伺服器的執行結果的。
一起看下sendSayHi是如何?的(代碼被我精簡,這保留了關鍵區段,完整代碼可以自己通過thrift命令產生查看)
func (p *RpcServiceClient) sendSayHi(name string) (err error) { oprot := p.OutputProtocol //擷取傳輸協議 if err = oprot.WriteMessageBegin("SayHi", thrift.CALL, p.SeqId); err != nil { //發送SayHi字元創,告訴伺服器將來執行的函數 return } args := RpcServiceSayHiArgs{ //構建參數 Name: name, } if err = args.Write(oprot); err != nil { //將參數發送給伺服器 return } if err = oprot.WriteMessageEnd(); err != nil { //通知伺服器發送完畢 return } return oprot.Flush()}
通過這樣的一系列資料轉送,伺服器通過路由解析,便可以正確的知道該執行哪個函數了。thrift的精髓也正在此,實現了rpc架構,用戶端只需要簡單的調用client.SayHi(),不必知道這是本地調用還是遠程調用。
好了,既然請求發出了,我們現在當然看看伺服器是如何響應的,在源碼中,有一個函數是專門響應用戶端請求的:
func (p *RpcServiceProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException)
前面講解伺服器端是如何建立的時候講到過一個processRequests()函數,它在client串連上server的時候會被server調用。我們看看源碼:
func (p *TSimpleServer) processRequests(client TTransport) error { processor := p.processorFactory.GetProcessor(client) //.... for { ok, err := processor.Process(inputProtocol, outputProtocol) if err{ //.... } } return nil}
在去除無關代碼之後我們看到,伺服器首先擷取用戶端的processor,然後調用processor的Process函數,從而執行響應用戶端的請求。
看看Process函數具體是如何?的:
func (p *RpcServiceProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { name, _, seqId, err := iprot.ReadMessageBegin() //擷取用戶端請求執行的函數名稱 if err != nil { return false, err } if processor, ok := p.GetProcessorFunction(name); ok { return processor.Process(seqId, iprot, oprot) //執行 } //...}
要注意的是,函數中用紅色標註的Process是另外一個函數,這裡可不是遞迴。兩個Process函數的聲明是不一樣的:
func (p *RpcServiceProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) //RpcServiceProcessor是server的processorfunc (p *rpcServiceProcessorSayHi) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) //rpcServiceProcessorSayHi是具體的handler,
現在到了最關鍵的時候了,我們看看handler是如何執行process的:
func (p *rpcServiceProcessorSayHi) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { args := RpcServiceSayHiArgs{} //構建參數 if err = args.Read(iprot); err != nil { //讀取用戶端發來的參數 //處理err } iprot.ReadMessageEnd() //讀取用戶端的結束訊息 result := RpcServiceSayHiResult{} var retval string var err2 error if retval, err2 = p.handler.SayHi(args.Name); err2 != nil { //執行函數 //..處理err } else { result.Success = &retval } //...將result發送給用戶端,流程和client發送請求類似,client通過recvSayHi()函數接受result return true, err}
現在,伺服器和用戶端究竟是如何工作的。你明白了嗎
轉載請註明出處,謝謝