前言
上一篇對gRPC進行簡單介紹,並通過示例體驗了一下開發過程。接下來說說實際開發常用功能,如:gRPC的四種模式、gRPC整合JWT做認證和授權等。
正文
1. gRPC四種模式服務
以下案例演示,服務端用微軟提供的模板建立,客戶端使用Winform程式演示,基於.NetCore3.1版本。具體建立步驟在上一篇說的很細了(gRPC趁現在還沒大火,搶先了解一下),接下來就直接搞重點;這裡就模仿一個學生服務,包含增、刪、改、查方法,下面是用到的proto檔案的全部內容,後續的例項就單獨標出重點即可。
syntax = "proto3"; //指定版本
// 定義名稱空間
option csharp_namespace = "Grpc.Server.Demo";
// 指定包名,避免衝突
package user;
// 定義Student 的 message型別
message Student {
string UserName = 1;
int32 Age=2;
string addr = 3;
}
// 公共返回型別
message CommonResponse{
int32 code =1;
string msg=2;
}
// 新增學生時傳遞的型別
message AddStudentRequest{
Student student=1;
}
// 查詢學生時傳遞的型別
message QueryStudentRequest
{
string UserName=1;
}
// 查詢全部學生,沒有條件,但也需要一個空的message
message QueryAllStudentRequest
{
}
// 上傳圖片
message UploadImgRequest{
bytes data = 1;
}
message StudentResponse {
Student student =1;
}
message TokenRequest{
string UserName=1;
string UserPwd=2;
}
message TokenResponse{
string Token =1;
}
// 約定需要提供的服務方法
service StudentService{
rpc GetToken(TokenRequest) returns (TokenResponse);
// 簡單模式,查詢
rpc GetStudentByUserName(QueryStudentRequest) returns (StudentResponse);
// 服務端流模式
rpc GetAllStudent(QueryAllStudentRequest) returns (stream StudentResponse);
// 客戶端流模式
rpc UploadImg(stream UploadImgRequest) returns (CommonResponse);
// 雙向流模式
rpc AddManyStudents(stream AddStudentRequest) returns (stream StudentResponse);
}
整體的專案結構如下:
1.1 簡單模式
和現在http方式類似:客戶端發出單個請求,服務端返回單個響應。
關於簡單模式,請求引數和返回引數都是一般message型別,在上一篇中演示的模式就是簡單模式,歸納如下步驟;
服務端
-
增加一個student.proto檔案,在檔案中定義服務,編譯自動生成對應程式碼
定義服務格式:
rpc 方法名(請求型別) returns (返回型別);
-
新建StudentDemoService類,繼承生成的程式碼類,開始寫業務
-
在Startup檔案中將服務方法暴露出去(這裡寫一次即可,後續就不重複說了)
到這服務端就寫完啦,其實和原來寫WebApi介面一樣便捷。
客戶端
使用Winform的形式舉例演示客戶端,在建立專案時,直接選擇Winform模板即可,簡單設計了一下介面,如下:
-
引入對應的包,將服務端的proto檔案都拷過來(服務端和客戶端proto檔案一致)
如果編譯沒自動生成程式碼,需要檢查是否引入對應的包,是否設定了student.proto檔案的屬性,如果這塊還不瞭解,點這裡(gRPC趁現在還沒大火,搶先了解一下)先熟悉以下開發過程。
-
在winform設計模式下,雙擊按鈕增加點選事件,開始寫業務邏輯
-
執行看效果
先執行服務端,在執行客戶端,輸入條件,點選簡單模式按鈕,效果如下:
這裡查不到資料,客戶端程式報異常(別罵我程式碼寫的不嚴謹,小夥伴處理一下就好啦)
1.2 服務端流模式
客戶端發起一個請求到服務端,服務端返回連續的資料流;一般用在服務端分批返回資料的情況,客戶端能持續接收服務端的資料。
服務端
-
在student.proto檔案中增加服務,編譯自動生成程式碼
定義服務格式:
rpc 方法名(請求型別) returns (stream 返回型別);
-
在StudentDemoService類中,重寫方法寫業務邏輯程式碼
注意點:
- 就算請求不需要引數,也需要一個空的message型別;
- 這裡返回的資料可以分批傳送,複用連線,提高效率;
客戶端
-
student.proto檔案保證和服務端一樣同步新增相應的內容
這裡就不截圖了,小夥伴可以通過拷貝或是引用的方式保證檔案一樣就行啦
-
在winform設計模式下,雙擊服務端流模式按鈕增加點選事件,開始寫業務邏輯
-
執行看效果
先執行服務端,在執行客戶端,點選服務端流模式按鈕,效果如下:
1.3 客戶端流模式
客戶端將連續的資料流傳送到服務端,服務端返回一個響應;用在客戶端傳送多次請求到服務端情況,如分段上傳圖片場景等。
服務端
-
在student.proto檔案中增加服務,編譯自動生成程式碼
定義服務格式:
rpc 方法名(stream 請求型別) returns (返回型別);
-
在StudentDemoService類中,重寫方法寫業務邏輯程式碼
客戶端
-
student.proto檔案保證和服務端一樣同步新增相應的內容
這裡就不截圖了,小夥伴可以通過拷貝或是引用的方式保證檔案一樣就行啦。
-
在winform設計模式下,雙擊客戶端流模式按鈕增加點選事件,開始寫業務邏輯。程式碼稍多,不截圖了,不然圖片大要失真,直接上程式碼吧。
private async void btn_client_Click(object sender, EventArgs e) { // 用於存放選擇的檔案路徑 string filePath = string.Empty; // 開啟檔案選擇對話方塊 if (this.openFileDialog1.ShowDialog() == DialogResult.OK) { filePath = this.openFileDialog1.FileName; } if(string.IsNullOrEmpty(filePath)) { this.txt_result.Text = "請選擇檔案"; return; } //1、建立grpc客戶端 using var channel = GrpcChannel.ForAddress("https://localhost:5001"); var grpcClient = new StudentService.StudentServiceClient(channel); //2、讀取選擇的檔案 FileStream fileStream = File.OpenRead(filePath); //3、通過客戶端請求流將檔案流傳送的服務端 using var call = grpcClient.UploadImg(); var clientStream = call.RequestStream; //4、迴圈傳送,指定傳送完檔案 while(true) { // 一次最多傳送1024位元組 byte[] buffer = new byte[1024]; int nRead = await fileStream.ReadAsync(buffer, 0, buffer.Length); // 直到讀不到資料為止,即檔案已經傳送完成,即退出傳送 if(nRead==0) { break; } // 5、將每次讀取到的檔案流通過客戶端流傳送到服務端 await clientStream.WriteAsync(new UploadImgRequest { Data = ByteString.CopyFrom(buffer) }); } // 6、傳送完成之後,告訴服務端傳送完成 await clientStream.CompleteAsync(); // 7、接收返回結果,並顯示在文字框中 var res = await call.ResponseAsync; this.txt_result.Text = $"上傳返回Code:{res.Code},Msg:{res.Msg}"; }
-
執行看效果
先執行服務端,在執行客戶端,點選客戶端流模式按鈕,效果如下:
在彈框中選擇一個jpg的圖片(因為方便演示,服務端固定寫為jpg了),如下:
選擇完圖片就開始上傳了,如下:
是不是已經感覺到gRPC的優點了,資料傳輸量及方式有沒有先進一點。
1.4 雙向流模式
雙向流就是服務端流和客戶端流的整合,請求和返回都可以通過流的方式互動。
服務端
-
在student.proto檔案中增加服務,編譯自動生成程式碼
定義服務格式:
rpc 方法名(stream 請求型別) returns (stream 返回型別);
-
在StudentDemoService類中,重寫方法寫業務邏輯程式碼
客戶端
-
student.proto檔案保證和服務端一樣同步新增相應的內容
這裡就不截圖了,小夥伴可以通過拷貝或是引用的方式保證檔案一樣就行啦
-
在winform設計模式下,雙擊雙向流模式按鈕增加點選事件,開始寫業務邏輯。
-
執行看效果
先執行服務端,在執行客戶端,點選雙向流模式按鈕,效果如下:
點選服務端流按鈕檢視全部數,看看是否新增成功:
gRPC的四種模式就簡單介紹這麼多,小夥伴可以根據提供的思路應用到專案中。 在演示案例中是不是感受到gRPC相比WebApi有更多的選擇和優勢,另外通過服務端的控制檯可以看到,互動使用的是HTTP/2協議,小夥伴感興趣可以去了解一下:
2. gRPC整合JWT認證
和WebAPI一樣,如果提供的服務介面“裸奔”,那麼風險是很大的;關於這點,之前針對WebApi認證和授權分享了兩篇文章,傳送門在這(跟我一起學.NetCore之WebApi介面裸奔有風險(Jwt)、跟我一起學.NetCore之熟悉的介面許可權驗證不能少(Jwt))。
其實gRPC整合JWT做認證授權,和原來WebApi整合方式差不多一樣,太細節的描述小夥伴可以查閱上面兩篇文章;接下來的需求目的就是把上面提供的服務保護起來,只有認證通過才能呼叫。
2.1 引入Jwt相關包,註冊服務,中介軟體管道增加認證流程
-
在gRPC服務端專案中引入Jwt相關包
Microsoft.AspNetCore.Authentication.JwtBearer
-
在Startup檔案中註冊相關服務
-
在Startup檔案中增加認證流程
-
在服務上增加Authorize特性標識
執行看效果:
通過呼叫結果得知,現在提供的gRPC服務已經受到保護,需要持有對應身份的請求才能訪問,所以接下來就需要服務端提供一個獲取身份Token的方法。
2.2 服務端增加獲取Token的服務方法
-
服務端增加獲取Token的服務方法
現在student.proto檔案中增加獲取Token的相關約定,編譯自動生成對應程式碼,如下:
重寫方法,編寫獲取Token的邏輯,如下:
生成token的核心邏輯為方法GenerateToken,如下:
到這,服務端生成Token的方法就搞定了,接下開始讓客戶端獲取並使用即可。
2.3 客戶端獲取Token並使用
-
確保student.proto檔案內容和服務端的一致,然後編譯自動生成程式碼。
這裡就不截圖了,小夥伴可以通過拷貝或是引用的方式保證檔案一樣就行啦
-
這裡將服務端流模式的按鈕複製一個出來,在其點選事件中增加帶Token的邏輯
獲取Token的邏輯就是簡單的呼叫服務端的方法,如下:
這樣就完成Jwt的整合了,服務端、客戶端都執行起來看一下:
到這裡,gRPC整合Jwt做認證的全部演示就完成了,除了呼叫方式和客戶端傳遞Token的方式不一樣,其他的都和WebApi使用方式一樣。
重點:這裡gRPC可以通過Metadata進行傳遞資料,和之前WebApi的請求頭很像,可以根據自己需求進行任意封裝傳遞。
3. gRPC許可權驗證思路提一下
上面只是進行了認證,還沒有對服務方法許可權進行管控,只要認證通過就能呼叫全部服務方法;在實際應用場景中更希望的是分配啥許可權才能呼叫對應的服務,所以許可權管控少不了。
gRPC的許可權管控和WebApi的管控方式一樣,同樣可以使用策略的方式進行許可權驗證。gRPC同樣能獲取到請求方法的Url(包名.服務名.方法名),這樣就可以以這種規則進行許可權配置,然後驗證即可。詳細步驟可以參考跟我一起學.NetCore之熟悉的介面許可權驗證不能少(Jwt),接下來就來說說主要步驟:
3.1 使用動態許可權策略形式
-
增加一個PermissionRequirement.cs檔案,如下:
-
增加一個PermissionHandler.cs檔案,整合自AuthorizationHandler,然後重寫處理方法,核心程式碼如下:
-
Startup.cs檔案中註冊相關服務,如下:
-
在Authorize特性中傳遞對應的策略名稱,如下:
-
這裡模擬配置許可權,在獲取token方法中內建幾條對應使用者的許可權資料
-
執行看效果,如下:
最後驗證成功就正常返回結果,如果驗證不成功就返回失敗,客戶端就報異常,如下:
原始碼地址:https://gitee.com/CodeZoe/g-rpc/tree/master
後續會把其他程式碼也整理到碼雲上。
總結
關於gRPC實際應用場景常用的功能就先說到這吧,以上案例演示只是提供思路,小夥伴使用時可以根據對應的需求進行擴充套件和處理。
既然聊到了服務間通訊,分散式事務肯定是避不開的,下一篇開始說說分散式事務相關的點。
一個被程式搞醜的帥小夥,關注"Code綜藝圈",和我一起學~~~