日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

當涉及到REST API時,JSON(JAVAScript對象表示法)已經(jīng)成為數(shù)據(jù)交換的格式。很久以前,開發(fā)人員放棄了XML,轉(zhuǎn)而支持JSON,因為JSON緊湊,無模式,易于閱讀且易于在線傳輸。

JSON的無模式性質(zhì)確保您可以添加或刪除字段,并且仍然擁有有效的JSON。但是,這也意味著,由于添加或刪除了字段,您現(xiàn)在功能全面的客戶端將開始失敗。當您具有微服務(wù)體系結(jié)構(gòu)并且有100個服務(wù)通過JSON相互通信并且您不小心更改了其中一個服務(wù)的JSON響應(yīng)時,此問題會放大。

此外,JSON通過重復字段名(如果你使用的是陣列)發(fā)生不必要的額外空間,變得相當難讀的,一旦你開始建立你的數(shù)據(jù)結(jié)構(gòu)。

2001年,google開發(fā)了一種內(nèi)部,平臺和語言獨立的數(shù)據(jù)序列化格式,稱為Protobuf(協(xié)議緩沖區(qū)的縮寫),以解決JSON的所有缺點。Protobuf的設(shè)計目標是簡化和提高速度。

在本文中,我將分享什么是Protobuf,以及在REST API中替換JSON如何顯著簡化客戶端和服務(wù)器之間的數(shù)據(jù)序列化。

表中的內(nèi)容

  1. Protobuf是什么
  2. 工具
  3. Protobuf定義
  4. 創(chuàng)建REST端點
  5. 使用REST端點
  6. 與JSON相比
  7. 結(jié)論

1. Protobuf是什么

Protobuf的維基百科說:

協(xié)議緩沖區(qū)(Protobuf)是一種序列化結(jié)構(gòu)化數(shù)據(jù)的方法。在開發(fā)程序時,通過線路相互通信或存儲數(shù)據(jù)是很有用的。該方法涉及描述某些數(shù)據(jù)結(jié)構(gòu)的接口描述語言和從該描述生成源代碼的程序,用于生成或解析表示結(jié)構(gòu)化數(shù)據(jù)的字節(jié)流。

在Protobuf中,開發(fā)人員在.proto文件中定義數(shù)據(jù)結(jié)構(gòu)(稱為消息),然后在編譯protoc器的幫助下編譯為代碼。該編譯器帶有用于多種語言(來自Google和社區(qū))的代碼生成器,并生成用于存儲數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)和用于對其進行序列化和反序列化的方法。

Protobuf消息被序列化為二進制格式,而不是諸如JSON之類的文本,因此Protobuf中的消息根本不是人類可讀的。由于二進制性質(zhì),Protobuf消息可以壓縮,并且比等效的JSON消息占用更少的空間。

一旦完成服務(wù)器的實現(xiàn),就可以.proto與客戶端共享文件(就像共享API期望并返回的JSON模式一樣),它們可以利用相同的代碼生成來使用消息。

2.工具

我們需要安裝以下工具來遵循本教程。

  1. VS代碼或您最喜歡的代碼編輯器。
  2. Golang編譯器和工具(我們將在Go中編寫服務(wù)器和客戶端)
  3. [protoc](https://github.com/protocolbuffers/protobuf/releases) protobuf編譯器。

請遵循每個工具的安裝說明。為了簡潔起見,我跳過了此處的說明,但是如果您遇到任何錯誤,請告訴我,我們將很樂意為您提供幫助。

3. Protobuf定義

在本節(jié)中,我們將創(chuàng)建一個.proto文件,在整個演示過程中將使用該文件。該原始文件將包含兩個消息EchoRequest和EchoResponse。

然后,我們將創(chuàng)建REST端點接受EchoRequest并使用進行回復EchoResponse。然后,我們將使用REST端點創(chuàng)建一個客戶端(也在Go中)。

在開始之前,我希望您注意有關(guān)該項目目錄結(jié)構(gòu)的一些事情。

  1. 我已經(jīng)在文件夾github.com/kaysush中創(chuàng)建了一個文件$GOPATH/src夾。$GOPATH安裝go編譯器和工具時會設(shè)置變量。
  2. 我將項目文件夾protobuf-demo放入github.com/kaysush。

您可以在下圖中看到目錄結(jié)構(gòu)。

$GOPATH
 ├── bin
 ├── pkg
 └── src
 └── github.com
 └── kaysush
 └── protobuf-demo
 ├── server
 │ └── test.go
 ├── client
 └── proto
 └── echo
 ├── echo.proto 
 └── echo.pb.go

創(chuàng)建一個echo.proto文件。

syntax = "proto3";
package echo;
option go_package="echo";
message EchoRequest {
 string name = 1;
}
message EchoResponse {
 string message = 1;
}

echo.proto

將proto文件編譯為golang代碼。

protoc echo.proto --go_out=.

這將生成一個echo.pb.go文件,該文件具有將我們的消息定義為的go代碼struct。

作為測試,我們將查看封送和反封送消息是否正常工作。

package main
import (
 "fmt"
 "log"
 "github.com/golang/protobuf/proto"
 "github.com/kaysush/protobuf-demo/proto/echo" //<-- Take a note that I've created my code folder in $GOPATH/src
)
func main() {
 req := &echo.EchoRequest{Name: "Sushil"}
 data, err := proto.Marshal(req)
 if err != nil {
 log.Fatalf("Error while marshalling the object : %v", err)
 }
 res := &echo.EchoRequest{}
 err = proto.Unmarshal(data, res)
 if err != nil {
 log.Fatalf("Error while un-marshalling the object : %v", err)
 }
 fmt.Printf("Value from un-marshalled data is %v", res.GetName())
}

test.go

執(zhí)行它。

go run test.go

您應(yīng)該看到以下輸出。

Value from un-marshalled data is Sushil

這表明我們的Protobuf定義運行良好。在下一節(jié)中,我們將實現(xiàn)REST端點并接受Protobuf消息作為請求的有效負載。

4.創(chuàng)建REST端點

Golang的net.http軟件包足以創(chuàng)建REST API,但為了使我們更容易一點,我們將使用該[gorilla/mux](https://www.gorillatoolkit.org/pkg/mux)軟件包來實現(xiàn)REST端點。

使用以下命令安裝軟件包。

go get github.com/gorilla/mux

server.go在server文件夾中創(chuàng)建一個文件,然后開始編碼。

package main
import (
 "fmt"
 "io/ioutil"
 "log"
 "net/http"
 "time"
 "github.com/golang/protobuf/proto"
 "github.com/gorilla/mux"
 "github.com/kaysush/protobuf-demo/proto/echo"
)
func Echo(resp http.ResponseWriter, req *http.Request) {
 contentLength := req.ContentLength
 fmt.Printf("Content Length Received : %vn", contentLength)
 request := &echo.EchoRequest{}
 data, err := ioutil.ReadAll(req.Body)
 if err != nil {
 log.Fatalf("Unable to read message from request : %v", err)
 }
 proto.Unmarshal(data, request)
 name := request.GetName()
 result := &echo.EchoResponse{Message: "Hello " + name}
 response, err := proto.Marshal(result)
 if err != nil {
 log.Fatalf("Unable to marshal response : %v", err)
 }
 resp.Write(response)
}
func main() {
 fmt.Println("Starting the API server...")
 r := mux.NewRouter()
 r.HandleFunc("/echo", Echo).Methods("POST")
 server := &http.Server{
 Handler: r,
 Addr: "0.0.0.0:8080",
 WriteTimeout: 2 * time.Second,
 ReadTimeout: 2 * time.Second,
 }
 log.Fatal(server.ListenAndServe())
}

server.go

當前目錄如下所示。

$GOPATH
 ├── bin
 ├── pkg
 └── src
 └── github.com
 └── kaysush
 └── protobuf-demo
 ├── server
 │ ├── test.go
 │ └── server.go
 ├── client
 └── proto
 └── echo
 ├── echo.proto 
 └── echo.pb.go

該Echo函數(shù)的代碼應(yīng)易于理解。我們http.Request使用讀取字節(jié)iotuil.ReadAll,然后從中讀取Unmarshal字節(jié)。EchoRequest``Name

然后,我們按照相反的步驟來構(gòu)造一個EchoResponse。

在Main()函數(shù)中,我們定義了一條路由/echo,該路由應(yīng)接受POST方法并通過調(diào)用Echo函數(shù)來處理請求。

啟動服務(wù)器。

go run server.go

您應(yīng)該會看到消息 Starting API server...

具有/echo端點接受POST功能的REST-ish API(因為我們未遵循POST請求的REST規(guī)范)已準備好接受來自客戶端的Protobuf消息。

5.使用REST端點

在本節(jié)中,我們將實現(xiàn)使用/echo端點的客戶端。

我們的客戶端和服務(wù)器都在相同的代碼庫中,因此我們不需要從proto文件中重新生成代碼。在實際使用中,您將proto與客戶端共享文件,然后客戶端將以其選擇的編程語言生成其代碼文件。

client.go在client文件夾中創(chuàng)建一個文件。

package main
import (
 "bytes"
 "fmt"
 "io/ioutil"
 "log"
 "net/http"
 "github.com/golang/protobuf/proto"
 "github.com/kaysush/protobuf-demo/proto/echo"
)
func makeRequest(request *echo.EchoRequest) *echo.EchoResponse {
 req, err := proto.Marshal(request)
 if err != nil {
 log.Fatalf("Unable to marshal request : %v", err)
 }
 resp, err := http.Post("http://0.0.0.0:8080/echo", "Application/x-binary", bytes.NewReader(req))
 if err != nil {
 log.Fatalf("Unable to read from the server : %v", err)
 }
 respBytes, err := ioutil.ReadAll(resp.Body)
 if err != nil {
 log.Fatalf("Unable to read bytes from request : %v", err)
 }
 respObj := &echo.EchoResponse{}
 proto.Unmarshal(respBytes, respObj)
 return respObj
}
func main() {
 request := &echo.EchoRequest{Name: "Sushil"}
 resp := makeRequest(request)
 fmt.Printf("Response from API is : %vn", resp.GetMessage())
}

client.go

客戶應(yīng)該更容易理解。我們正在使用http.Post將Protobuf字節(jié)發(fā)送到我們的API服務(wù)器,并讀回響應(yīng),然后將Unmarshal其發(fā)送給EchoResponse。

立即運行客戶端。

go run client.go

您應(yīng)該看到服務(wù)器的響應(yīng)。

Response from API is : Hello Sushil

6.與JSON相比

我們已經(jīng)成功實現(xiàn)了使用Protobuf而不是JSON的API。

在本節(jié)中,我們將實現(xiàn)一個終結(jié)點,該終結(jié)點EchoJsonRequest在JSON中接受類似內(nèi)容,并在JSON中也進行響應(yīng)。

我已經(jīng)structs為JSON 實現(xiàn)了另一個程序包。

package echojson
type EchoJsonRequest struct {
 Name string
}
type EchoJsonResponse struct {
 Message string
}

echo.json.go

然后將新功能添加到server.go。

func EchoJson(resp http.ResponseWriter, req *http.Request) {
 contentLength := req.ContentLength
 fmt.Printf("Content Length Received : %vn", contentLength)
 request := &echojson.EchoJsonRequest{}
 data, err := ioutil.ReadAll(req.Body)
 if err != nil {
 log.Fatalf("Unable to read message from request : %v", err)
 }
 json.Unmarshal(data, request)
 name := request.Name
 result := &echojson.EchoJsonResponse{Message: "Hello " + name}
 response, err := json.Marshal(result)
 if err != nil {
 log.Fatalf("Unable to marshal response : %v", err)
 }
 resp.Write(response)
}

server.go

在中為此新功能添加綁定main()。

r.HandleFunc("/echo_json", EchoJson).Methods("POST")

讓我們修改客戶端,以將重復的請求發(fā)送到Protobuf和JSON端點,并計算平均響應(yīng)時間。

package main
import (
 "bytes"
 "encoding/json"
 "fmt"
 "io/ioutil"
 "log"
 "net/http"
 "time"
 "github.com/golang/protobuf/proto"
 "github.com/kaysush/protobuf-demo/proto/echo"
 "github.com/kaysush/protobuf-demo/proto/echojson"
)
func makeRequest(request *echo.EchoRequest) *echo.EchoResponse {
 req, err := proto.Marshal(request)
 if err != nil {
 log.Fatalf("Unable to marshal request : %v", err)
 }
 resp, err := http.Post("http://0.0.0.0:8080/echo", "application/json", bytes.NewReader(req))
 if err != nil {
 log.Fatalf("Unable to read from the server : %v", err)
 }
 respBytes, err := ioutil.ReadAll(resp.Body)
 if err != nil {
 log.Fatalf("Unable to read bytes from request : %v", err)
 }
 respObj := &echo.EchoResponse{}
 proto.Unmarshal(respBytes, respObj)
 return respObj
}
func makeJsonRequest(request *echojson.EchoJsonRequest) *echojson.EchoJsonResponse {
 req, err := json.Marshal(request)
 if err != nil {
 log.Fatalf("Unable to marshal request : %v", err)
 }
 resp, err := http.Post("http://0.0.0.0:8080/echo_json", "application/json", bytes.NewReader(req))
 if err != nil {
 log.Fatalf("Unable to read from the server : %v", err)
 }
 respBytes, err := ioutil.ReadAll(resp.Body)
 if err != nil {
 log.Fatalf("Unable to read bytes from request : %v", err)
 }
 respObj := &echojson.EchoJsonResponse{}
 json.Unmarshal(respBytes, respObj)
 return respObj
}
func main() {
 var totalPBTime, totalJSONTime int64
 requestPb := &echo.EchoRequest{Name: "Sushil"}
 for i := 1; i <= 1000; i++ {
 fmt.Printf("Sending request %vn", i)
 startTime := time.Now()
 makeRequest(requestPb)
 elapsed := time.Since(startTime)
 totalPBTime += elapsed.Nanoseconds()
 }
 requestJson := &echojson.EchoJsonRequest{Name: "Sushil"}
 for i := 1; i <= 1000; i++ {
 fmt.Printf("Sending request %vn", i)
 startTime := time.Now()
 makeJsonRequest(requestJson)
 elapsed := time.Since(startTime)
 totalJSONTime += elapsed.Nanoseconds()
 }
 fmt.Printf("Average Protobuf Response time : %v nano-secondsn", totalPBTime/1000)
 fmt.Printf("Average JSON Response time : %v nano-secondsn", totalJSONTime/1000)
}

運行服務(wù)器和客戶端。

我們的服務(wù)器記錄了請求的內(nèi)容長度,您可以看到Protobuf請求為8個字節(jié),而相同的JSON請求為17個字節(jié)

 

使用Protobuf增強您的REST API

 

 

JSON的請求大小是普通消息的兩倍

客戶端記錄Protobuf和JSON請求的平均響應(yīng)時間(以納秒為單位)(封送請求+發(fā)送請求+封送響應(yīng))。

我運行了client.go3次,盡管平均響應(yīng)時間差異很小,但我們可以看到Protobuf請求的平均響應(yīng)時間始終較小。

差異很小,因為我們的消息非常小,隨著消息大小的增加,將其取消編組為JSON的成本也隨之增加。

 

使用Protobuf增強您的REST API

 

 

多個比較請求

7.結(jié)論

在REST API中使用Protobuf而不是JSON可以導致更小的請求大小和更快的響應(yīng)時間。在我們的演示中,由于有效負載較小,因此響應(yīng)時間效果并不明顯,但是看到這種模式,可以肯定地說Protobuf的性能應(yīng)優(yōu)于JSON。

那里有它。在您的REST API中使用Protobuf替換JSON。

如果您發(fā)現(xiàn)我的代碼有任何問題或有任何疑問,請隨時發(fā)表評論。

直到快樂的編碼!:)

翻譯自:https://medium.com/@Sushil_Kumar/supercharge-your-rest-apis-with-protobuf-b38d3d7a28d3

分享到:
標簽:Protobuf
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數(shù)有氧達人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定