54.grpc實現文件上傳和下載

文章目錄

  • 一:簡介
    • 1. 什么是grpc
    • 2. 為什么我們要用grpc
  • 二:grpc的hello world
    • 1、 定義hello.proto文件
    • 2、生成xxx_grpc.pb.go文件
    • 3、生成xxx.pb.go結構體文件
    • 4、編寫服務代碼service.go
    • 5、編寫客戶端代碼client.go
  • 三、服務端流式傳輸:文件下載
    • 文件下載
  • 四、客戶端流式傳輸:文件上傳
    • 文件上傳
  • 五、雙向流:聊天

代碼地址:https://gitee.com/lymgoforIT/golang-trick/tree/master/32-grpc

一:簡介

1. 什么是grpc

gRpc 是一個高性能、開源和通用的 RPC 框架,面向移動和 HTTP2 設計。目前提供 C、Java 和 Go 語言版本,分別是:grpc, grpc-java, grpc-go. 其中 C 版本支持C, C++, Node.js, Python, Ruby, Objective-C, PHP C# 支持.
gRPC 基于 HTTP2 標準設計,帶來諸如雙向流、流控、頭部壓縮、單 TCP 連接上的多復用請求等特。這些特性使得其在移動設備上表現更好,更省電和節省空間占用。
參考: grpc官方文檔中文版

2. 為什么我們要用grpc

  • 生態好:背靠Google。還有比如nginx也對grpc提供了支持
  • 跨語言:跨語言,且自動生成sdk
  • 性能高:比如protobuf性能高過json, 比如http2.0性能高過http1.1
  • 強類型:編譯器就給你解決了很大一部分問題
  • 流式處理(基于http2.0):支持客戶端流式,服務端流式,雙向流式

二:grpc的hello world

在這里插入圖片描述

1、 定義hello.proto文件

syntax = "proto3"; // 指定proto版本,不指定時默認是proto2
package hello_grpc; // 指定默認包名// 指定golang包名,當編譯為pb.go時,這個包名會替換上面package指定的名字
option go_package = "/hello_grpc";message HelloRequest {string  name = 1;string message = 2;
}message HelloResponse {string  name = 1;string message = 2;
}// 定義rpc服務
service HelloService {// 定義函數rpc SayHello(HelloRequest) returns (HelloResponse) {}
}

2、生成xxx_grpc.pb.go文件

 protoc --proto_path=32-grpc/grpc_proto --go-grpc_out=32-grpc/grpc_proto 32-grpc/grpc_proto/hello.proto

解釋:

  1. protoc為我們安裝的protocol buffer 的命令
  2. --proto_path選項為指定proto文件所在的路徑,可以換為-I
  3. --go-grpc_out指明要生成的是grpc文件,且指明了生成后文件要放在哪個目錄
  4. 最后的參數為proto文件,可以寫多個,也可以寫.,表示當前目錄下所有proto文件

執行后如下:報紅的地方為找不到對應的結構體,所以我們還需要生成結構體的pb.go文件
在這里插入圖片描述

3、生成xxx.pb.go結構體文件

 protoc --proto_path=32-grpc/grpc_proto --go_out=32-grpc/grpc_proto 32-grpc/grpc_proto/hello.proto

命令和生成gprc文件幾乎一致,只是--go-grpc_out選項換成了--go_out選項而已

在這里插入圖片描述

注意:

  1. 23兩個生成pb.go的命令也可以寫成一個命令,如下
 protoc --proto_path=32-grpc/grpc_proto --go_out=32-grpc/grpc_proto --go-grpc_out=32-grpc/grpc_proto 32-grpc/grpc_proto/hello.proto
  1. pb描述和rpc?法之前舊版?成是在?個?件中,?前新版本pb?法已經分離?成的?件格式特征如下:
    在這里插入圖片描述

4、編寫服務代碼service.go

在這里插入圖片描述

package mainimport ("context""fmt""golang-trick/32-grpc/grpc_proto/hello_grpc""net""google.golang.org/grpc""google.golang.org/grpc/grpclog"
)type HelloService struct {hello_grpc.UnimplementedHelloServiceServer
}func (h HelloService) SayHello(ctx context.Context, request *hello_grpc.HelloRequest) (*hello_grpc.HelloResponse, error) {fmt.Println(request)return &hello_grpc.HelloResponse{Name:    "lym",Message: "ok",}, nil
}func main() {// 監聽端口listen, err := net.Listen("tcp", ":8080")if err != nil {grpclog.Fatalf("Failed to listen err:%v", err)}// 創建一個grpc服務器實例s := grpc.NewServer()server := HelloService{}// 將server結構體注冊為grpc服務hello_grpc.RegisterHelloServiceServer(s, &server)fmt.Println("grpc server running:9090")// 開始處理客戶端請求err = s.Serve(listen)
}

解釋:
HelloService 是我們自定義的結構體,需要實現hello_grpc中的HelloServiceServer 接口,但是該接口包含一個私有的mustEmbedUnimplementedHelloServiceServer方法,導致無法實現該接口,目前解決辦法就是讓我們的結構體包含hello_grpc.UnimplementedHelloServiceServer,從而實現hello_grpc中的HelloServiceServer 接口

// HelloServiceServer is the server API for HelloService service.
// All implementations must embed UnimplementedHelloServiceServer
// for forward compatibility
type HelloServiceServer interface {// 定義函數SayHello(context.Context, *HelloRequest) (*HelloResponse, error)mustEmbedUnimplementedHelloServiceServer()
}

運行服務端

在這里插入圖片描述

5、編寫客戶端代碼client.go

package mainimport ("context""fmt""golang-trick/32-grpc/grpc_proto/hello_grpc""log""google.golang.org/grpc"
)func main() {addr := ":8080"//使用grpc.Dial 創建一個到指定的地址的 grpc 連接conn, err := grpc.Dial(addr, grpc.WithInsecure())if err != nil {log.Fatalf(fmt.Sprintf("grpc connect adddr[%s] failed,err:%v", addr, err))}defer conn.Close()// 初始化客戶端client := hello_grpc.NewHelloServiceClient(conn)// 調用遠程服務端方法resp, err := client.SayHello(context.Background(), &hello_grpc.HelloRequest{Name:    "lym",Message: "我是客戶端",})fmt.Println(resp, err)}

運行客戶端

成功收到服務端的響應
在這里插入圖片描述
且服務端也打印出了客戶端發來的請求
在這里插入圖片描述

三、服務端流式傳輸:文件下載

grpc共有四種傳輸方式:

  • 普通交互:如上面hello world示例,客戶端發送一次請求,服務端響應一次。
  • 服務端流式:客戶端發送一次請求,服務端響應多次
  • 客戶端流式:客戶端發送多次請求,服務端響應一次
  • 雙向流式:客戶端和服務端有問有答一樣

而上面四次方式proto文件寫法上區別就在于stream關鍵字的有無以及所在位置

如普通式:

// 定義rpc服務
service Service {// 定義函數rpc SayHello(Request) returns (Response) {}
}

服務端流式:

// 定義rpc服務
service ServiceStream {// 定義函數rpc SayHello(Request) returns (stream Response) {}
}

文件下載

因為要下載的文件可能很大,服務端不能一次就把整個文件響應回去,因此需要用到服務端流式,多次發送,目錄結構以及文件大致如下:
在這里插入圖片描述

1、首先編寫stream.proto文件,注意響應多了stream關鍵字

syntax = "proto3"; // 指定proto版本,不指定時默認是proto2
package stream; // 指定默認包名// 指定golang包名,當編譯為pb.go時,這個包名會替換上面package指定的名字
option go_package = "/stream";message Request {string name = 1;
}message FileResponse {string file_name = 1;bytes content = 2;
}service ServiceStream {rpc DownLoadFile(Request) returns (stream FileResponse){};
}

2、生成pb.go文件,這個就不贅述了

 protoc --proto_path=32-grpc/grpc_proto --go_out=32-grpc/grpc_proto --go-grpc_out=32-grpc/grpc_proto 32-grpc/grpc_proto/stream.proto

3、服務端代碼stream_service.go。看代碼注釋即可, 不難

package mainimport ("fmt""golang-trick/32-grpc/grpc_proto/stream""io""net""os""google.golang.org/grpc""google.golang.org/grpc/grpclog"
)type ServiceStream struct {stream.UnimplementedServiceStreamServer
}func (s ServiceStream) DownLoadFile(request *stream.Request, resp stream.ServiceStream_DownLoadFileServer) error {fmt.Println(request)// 獲取要下載的文件file, err := os.Open("32-grpc/static/prometheus+granfana企業級監控實戰v5.pdf")if err != nil {return err}defer file.Close()for {buf := make([]byte, 1024)_, err := file.Read(buf)if err == io.EOF {break}if err != nil {break}// 可以通過接口上的resp對象的Send方法不斷給客戶端響應resp.Send(&stream.FileResponse{Content: buf,})}// return 后表明本次響應結束,不會再Sendreturn nil}func main() {// 監聽端口listen, err := net.Listen("tcp", ":8080")if err != nil {grpclog.Fatalf("Failed to listen err:%v", err)}// 創建一個grpc服務器實例s := grpc.NewServer()server := ServiceStream{}// 將server結構體注冊為grpc服務stream.RegisterServiceStreamServer(s, &server)fmt.Println("grpc server running:8080")// 開始處理客戶端請求err = s.Serve(listen)
}

4、客戶端代碼client.go,請求下載文件

package mainimport ("bufio""context""fmt""golang-trick/32-grpc/grpc_proto/stream""io""log""os""google.golang.org/grpc"
)func main() {addr := ":8080"//使用grpc.Dial 創建一個到指定的地址的 grpc 連接conn, err := grpc.Dial(addr, grpc.WithInsecure())if err != nil {log.Fatalf(fmt.Sprintf("grpc connect adddr[%s] failed,err:%v", addr, err))}defer conn.Close()// 初始化客戶端client := stream.NewServiceStreamClient(conn)resp, err := client.DownLoadFile(context.Background(), &stream.Request{Name: "下載文件"})if err != nil {log.Fatalln(err)}file, err := os.OpenFile("32-grpc/static/下載的pdf文件.pdf", os.O_CREATE|os.O_WRONLY, 0600)if err != nil {log.Fatalln(err)}defer file.Close()writer := bufio.NewWriter(file)for {// 客戶端從服務端得到的響應是一個stream,所以可以通過Recv不斷接收,直到遇到io.EOF表明服務端響應完畢了recv, err := resp.Recv()if err == io.EOF {break}fmt.Println(fmt.Sprintf("寫入數據 %d 字節", len(recv.Content)))// 將每輪接收到的內容寫入到文件中writer.Write(recv.Content)}writer.Flush()
}

測試:運行服務端和客戶端后,如下,接收到了文件
在這里插入圖片描述

四、客戶端流式傳輸:文件上傳

文件上傳

實際上,客戶端流式和服務端流式思路基本是完全一致的,就是在request前加stream關鍵字即可,然后文件上傳和文件下載也是類似的,只是發送方變為了客戶端,然后服務端不斷的接收,知道收到io.EOF時,響應給客戶端接收成功的消息。文件結構大致如下:

1、proto文件,注意包名修改,以及request前加了stream關鍵字

syntax = "proto3"; // 指定proto版本,不指定時默認是proto2
package client_stream; // 指定默認包名// 指定golang包名,當編譯為pb.go時,這個包名會替換上面package指定的名字
option go_package = "/client_stream";message FileRequest {string file_name = 1;bytes content = 2; // 對應go的[]byte類型
}message Response {string text = 1;
}service ClientStream {rpc UploadFile(stream FileRequest) returns (Response){};
}

2、生成pb.go文件

 protoc --proto_path=32-grpc/grpc_proto --go_out=32-grpc/grpc_proto --go-grpc_out=32-grpc/grpc_proto 32-grpc/grpc_proto/client_stream.proto

3、服務端代碼file_upload_service.go

注意方法上響應只有errorresponse沒有寫在方法上,而是通過req.SendAndClose返回的響應結果

package mainimport ("bufio""fmt""golang-trick/32-grpc/grpc_proto/client_stream""io""log""net""os""google.golang.org/grpc""google.golang.org/grpc/grpclog"
)type FileUploadService struct {client_stream.UnimplementedClientStreamServer
}func (f FileUploadService) UploadFile(req client_stream.ClientStream_UploadFileServer) error {// 這里文件名我們寫死了,實際應該用客戶端傳過來的file, err := os.OpenFile("32-grpc/static/上傳的png文件.png", os.O_CREATE|os.O_WRONLY, 0600)if err != nil {log.Fatalln(err)}defer file.Close()writer := bufio.NewWriter(file)for {recv, err := req.Recv()if err == io.EOF {break}fmt.Println(fmt.Sprintf("寫入數據 %d 字節", len(recv.Content)))writer.Write(recv.Content)}writer.Flush()// 注意方法上響應只有error,response是從這里返回的,而沒有寫在方法上req.SendAndClose(&client_stream.Response{Text: "服務端接收完成啦!"})return nil
}func main() {// 監聽端口listen, err := net.Listen("tcp", ":8080")if err != nil {grpclog.Fatalf("Failed to listen err:%v", err)}// 創建一個grpc服務器實例s := grpc.NewServer()server := FileUploadService{}// 將server結構體注冊為grpc服務client_stream.RegisterClientStreamServer(s, &server)fmt.Println("grpc server running:8080")// 開始處理客戶端請求err = s.Serve(listen)
}

4、客戶端代碼file_upload_client.go

注意:全部上傳完成后,才告知服務端發送結束了,并通過resp, err := stream.CloseAndRecv()接收服務端的響應

package mainimport ("context""fmt""golang-trick/32-grpc/grpc_proto/client_stream""io""log""os""google.golang.org/grpc"
)func main() {addr := ":8080"//使用grpc.Dial 創建一個到指定的地址的 grpc 連接conn, err := grpc.Dial(addr, grpc.WithInsecure())if err != nil {log.Fatalf(fmt.Sprintf("grpc connect adddr[%s] failed,err:%v", addr, err))}defer conn.Close()// 初始化客戶端client := client_stream.NewClientStreamClient(conn)stream, err := client.UploadFile(context.Background())if err != nil {log.Fatalln(err)}file, err := os.Open("32-grpc/static/21.png")if err != nil {log.Fatalln(err)}defer file.Close()for {buf := make([]byte, 1024)_, err := file.Read(buf)if err == io.EOF {break}if err != nil {break}stream.Send(&client_stream.FileRequest{Content: buf,})}// 全部上傳完成后,在這里告知服務端發送結束了,并接收服務端的響應resp, err := stream.CloseAndRecv()fmt.Println(resp, err)}

啟動服務端和客戶端,可以看到上傳成功
在這里插入圖片描述

五、雙向流:聊天

雙向流能想到的最簡單的場景就是聊天,一來一回的,就是在proto文件的接口上requestresponse前都加上stream,具體如何使用就要用的時候再查下吧,哈哈哈

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/210384.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/210384.shtml
英文地址,請注明出處:http://en.pswp.cn/news/210384.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

AIOps、微服務和云平臺

數字景觀正在從整體轉向微服務、基于云的服務。企業和公司需要適應不斷變化的技術格局并跟上變化。系統變得越來越復雜并且不容易管理。我將嘗試解釋一些較新的架構方法、趨勢,并提供對 AIOps 的見解以及它如何幫助解決這個問題。 微服務 微服務架構正在成為最受歡…

什么是web組態?一文讀懂web組態

隨著工業4.0的到來,物聯網、大數據、人工智能等技術的融合應用,使得工業領域正在經歷一場深刻的變革。在這個過程中,web組態技術以其獨特的優勢,正在逐漸受到越來越多企業的關注和認可。那么,什么是web組態&#xff1f…

android-android源碼目錄

android源碼目錄 Android.bp art bionic bootable bootstrap.bash build build.sh compatibility cts dalvik developers development device external frameworks hardware IMAGE javaenv.sh kernel libcore libnativehelper Makefile mkcombinedroot mkimage_ab.sh mkimage.…

我的創作紀念日——一年

機緣 初心始于對技術的熱愛和分享知識的渴望。最初,我在一次練習中遇到了一些問題,通過解決這些問題并將解決方案記錄下來,我意識到分享經驗對自己和他人都非常有價值。于是,我開始在博客和社交平臺上記錄日常學習過程、撰寫技術…

uni-app 獲取PAD激光測溫方式 (uni-app安卓獲取廣播內容)

直接在onload執行下列代碼 var main plus.android.runtimeMainActivity(); //獲取activityvar context plus.android.importClass(android.content.Context); //上下文var receiver plus.android.implements(io.dcloud.feature.internal.reflect.BroadcastReceiver, {onRece…

動力未來:特斯拉 Model S 電池技術一覽

電動汽車是當今最具創新性和前景的交通工具之一,它們不僅能夠提供高效、環保的駕駛體驗,還能夠減少對化石燃料的依賴,促進可持續發展。在電動汽車領域,特斯拉 Model S 是一款引領潮流的產品,它以其豪華、強勁的性能和尖端的電池技術而聞名。本文將為您介紹特斯拉 Model S …

【springboot設計源碼】慶陽非物質文化遺產展示平臺課題背景、目的、意義、研究方法

該項目含有源碼、文檔、PPT、配套開發軟件、軟件安裝教程、項目發布教程等學習內容。 目錄 一、項目介紹: 二、文檔學習資料: 三、模塊截圖: 四、開發技術與運行環境: 五、代碼展示: 六、數據庫表截圖&#xff1…

即時通訊技術文集(第26期):實時音視頻技術合集(Part1) [共16篇]

為了更好地分類閱讀 52im.net 總計1000多篇精編文章,我將在每周三推送新的一期技術文集,本次是第26 期。 [- 1 -] 實時語音聊天中的音頻處理與編碼壓縮技術簡述 [鏈接] http://www.52im.net/thread-825-1-1.html [摘要] 在視頻或者音頻通話過程中&…

2023-12-09 LeetCode每日一題(下一個更大的數值平衡數)

2023-12-09每日一題 一、題目編號 2048. 下一個更大的數值平衡數二、題目鏈接 點擊跳轉到題目位置 三、題目描述 如果整數 x 滿足:對于每個數位 d ,這個數位 恰好 在 x 中出現 d 次。那么整數 x 就是一個 數值平衡數 。 給你一個整數 n &#xff0…

數據結構和算法專題---4、限流算法與應用

本章我們會對限流算法做個簡單介紹,包括常用的限流算法(計數器、漏桶算法、令牌桶案發、滑動窗口)的概述、實現方式、典型場景做個說明。 什么是限流算法 限流是對系統的一種保護措施。即限制流量請求的頻率(每秒處理多少個請求…

11_企業架構web服務器文件及時同步

企業架構web服務器的文件及時同步 學習目標和內容 1、能夠理解為何要服務器間文件同步 2、能夠簡單描述實現文件同步的幾種方式 3、能夠實現服務器文件實時同步的案例 一、同步文件介紹 1、服務器文件同步的必要性 根據業務發展需求,業務網站架構已經發展到以上模式…

Linux文件結構與文件權限

基于centos了解Linux文件結構 了解一下文件類型 Linux采用的一切皆文件的思想,將硬件設備、軟件等所有數據信息都以文件的形式呈現在用戶面前,這就使得我們對計算機的管理更加方便。所以本篇文章會對Linux操作系統的文件結構和文件權限進行講解。 首先…

單元測試Nunit的幾種斷言

Nunit提供了一些輔助函數用于確定好某個被測試函數是否正常工作。通常把這些函數稱為斷言 斷言是單元測試最基本的組成部分。因此,NUnit程序庫以Assert類的靜態方法的形式提供了不同形式的多種斷言 1. Assert.AreEqual:比較兩個值是否相等。用于比較數…

Qt生成動態鏈接庫并使用動態鏈接庫

項目結構 整個工程由一個主程序構成和一個模塊構成(dll)。整個工程的結構目錄如下 Define.priMyProject.proMyProject.pro.user ---bin ---MainProgrammain.cppMainProgram.proMainProgram.pro.userwidget.cppwidget.hwidget.ui ---MathDllMathDll.proMathDll.pro.userMyMath.…

Axios 攔截器實戰教程:簡單易懂

Axios 提供了一種稱為 “攔截器(interceptors)” 的功能,使我們能夠在請求或響應被發送或處理之前對它們進行全局處理。攔截器為我們提供了一種簡潔而強大的方式來轉換請求和響應、進行錯誤處理、添加認證信息等操作。在本文中,我…

Matlab 點云收縮L1中值(Weiszfeld算法)

文章目錄 一、簡介二、實現代碼三、實現效果參考資料一、簡介 對于之前的加權均值收縮方式,它存在一個很大的缺點,即容易受到噪聲的影響,因此這里我們采用另一種統計學方案:L1中值。其形式如下所示: 其中 x i x_i

MongoDB的條件操作符

本文主要介紹MongoDB的條件操作符。 目錄 MongoDB條件操作符1.比較操作符2.邏輯操作符3.元素操作符4.數組操作符5.文本搜索操作符 MongoDB條件操作符 MongoDB的條件操作符主要分為比較操作符、邏輯操作符、元素操作符、數組操作符、文本搜索操作符等幾種類型。 以下是這些操作…

拷貝實體類

文章目錄 方式一 : 方式二:(不常用) 方式一 : 將左邊的實體拷貝到右邊的實體中 import org.springframework.beans.BeanUtils; BeanUtils.copyProperties(memberAddress, resp);將右邊的實體拷貝到左邊的實體中 imp…

對String類的操作 (超細節+演示)

[本節目標] 1.認識String類 2.了解String類的基本用法 3.熟練掌握String類的常見操作 4.認識字符串常量池 5.認識StringBuffer和StringBuilder 1.String類的重要性 在C語言中已經涉及到字符串了,但是在C語言中要表示字符串只能使用字符數組或者字符指針&…

高速風筒安規方案中的安規測試及安規電路特性介紹--【其利天下技術】

作為家用電子產品,高速吹風筒做安規測試,過安規要求是必須保證的,一般電路要過安規測試,那么安規測試的目的是什么呢? 安規測試字面意思是安全規范測試,主要強調對使用人員的安全保護,也就是我…