【橘子分布式】gRPC(編程篇-中)

一、簡介

我們之前已經完成了對于api模塊的開發,也就是已經生成了基礎的類和對應的接口,現在我們需要完成的是client和server端的開發。其實如同thrift一樣,現在要做的就是實現我們之前定義的service里面的hello方法,里面寫我們的業務邏輯,然后通過grpc的server發布暴露出去給客戶端使用。

// 定義服務
service HelloService{/* 簡單rpc,參數為HelloRequest類型,返回類型為HelloResponse */rpc hello(HelloRequest) returns (HelloResponse){}
}

ok,我們就先來實現serve模塊,然后再實現client模塊。

二、server模塊

我們創建一個新的模塊叫做rpc-grpc-service,并且在其pom依賴中引入api這個公共模塊。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.levi</groupId><artifactId>rpc</artifactId><version>1.0-SNAPSHOT</version></parent><artifactId>rpc-grpc-service</artifactId><properties><maven.compiler.source>11</maven.compiler.source><maven.compiler.target>11</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>com.levi</groupId><artifactId>rpc-grpc-api</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies></project>

1、業務實現

ok,我們前面說過了,我們其實要實現的業務接口或者說重寫的接口實現方法是HelloServiceGrpc.HelloServiceImplBase這個Base的內部類,在這個里面我們覆蓋我們的業務方法。
我們重新來回顧一下我們的這個接口和方法。

// 定義請求接口參數
message HelloRequest{string name = 1;
}// 定義接口響應參數
message HelloResponse{string result = 1;
}// 定義服務
service HelloService{/* 簡單rpc,參數為HelloRequest類型,返回類型為HelloResponse */rpc hello(HelloRequest) returns (HelloResponse){}
}

我們看到我們的請求類里面是一個參數name,響應類里面是一個參數result,并且接口的方法叫做hello。ok,我們就來實現覆蓋這個方法。

/ 服務端實現類
public class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase {/*1. 接受client提交的參數  request.getParameter()2. 業務處理 service+dao 調用對應的業務功能。3. 提供返回值*/@Overridepublic void hello(HelloProto.HelloRequest request, StreamObserver<HelloProto.HelloResponse> responseObserver) {}
}
因為HelloServiceGrpc.HelloServiceImplBase不是一個接口,所以我們要繼承然后覆蓋方法。并且我們看到這個方法和我們當初定義的略有不同
第一個參數沒毛病就是HelloProto.HelloRequest,但是這個方法沒有返回值,他是個void。這就是grpc的規范,他的返回是通過第二個參數
StreamObserver<HelloProto.HelloResponse> responseObserver來給客戶端返回的,因為grpc有流式的返回,所以它是通過這個返回的,
如果弄成返回值就不方便以流的形式不斷的推給客戶端了。而且responseObserver的泛型就是我們定義的返回類型HelloProto.HelloResponse

于是我們就來實現這個方法。

/ 服務端實現類
public class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase {/*1. 接受client提交的參數  request.getParameter()2. 業務處理 service+dao 調用對應的業務功能。3. 提供返回值*/@Overridepublic void hello(HelloProto.HelloRequest request, StreamObserver<HelloProto.HelloResponse> responseObserver) {//1.接受client的請求參數,獲取我們定義的那個屬性nameString name = request.getName();//2.業務處理System.out.println("name parameter "+name);//3.封裝響應//3.1 創建相應對象的構造者HelloProto.HelloResponse.Builder builder = HelloProto.HelloResponse.newBuilder();//3.2 填充數據,填充返回值內容builder.setResult("hello method invoke ok");//3.3 封裝響應HelloProto.HelloResponse helloResponse = builder.build();// 4. 響應clientresponseObserver.onNext(helloResponse);// 5. 響應完成responseObserver.onCompleted();}
}

2、服務發布

現在我們實現了我們的業務,我們就要把這個服務接口發布出去給客戶端做rpc調用。
我們定義一個服務類,然后實現server,并且暴露端口。

package com.levi;import com.levi.service.HelloServiceImpl;
import io.grpc.Server;
import io.grpc.ServerBuilder;import java.io.IOException;public class GrpcServer1 {public static void main(String[] args) throws IOException, InterruptedException {//1. 綁定端口ServerBuilder<?> serverBuilder = ServerBuilder.forPort(9000);//2. 發布服務,這里可能會發布很多業務,我們這里就是一個HelloServiceImpl,實際可能還會有別的業務serverBuilder.addService(new HelloServiceImpl());//serverBuilder.addService(new UserServiceImpl());//3. 創建服務對象Server server = serverBuilder.build();// 啟動服務server.start();// 阻塞等待客戶端的連接訪問,底層其實就是nettyserver.awaitTermination();;}
}

此時我們就暴露出去我們的一個helo的業務實現了。

三、client模塊

我們創建一個名為rpc-grpc-client的模塊,并且引入api公共模塊。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.levi</groupId><artifactId>rpc</artifactId><version>1.0-SNAPSHOT</version></parent><artifactId>rpc-grpc-client</artifactId><properties><maven.compiler.source>11</maven.compiler.source><maven.compiler.target>11</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>com.levi</groupId><artifactId>rpc-grpc-api</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies></project>

我們現在已經把服務端的東西暴露在了9000這個端口,現在就可以在客戶端通過grpc的stub代理來訪問了。

package com.levi;import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;// client通過代理對象完成遠端對象的調用
public class GrpcClient1 {public static void main(String[] args) {//1.創建通信的管道,usePlaintext以普通文本進行訪問ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost", 9000).usePlaintext().build();//2.獲得代理對象 stub進行調用try {// 我們這里以阻塞的形式調用,也就是一直等返回值回來才往下走,其實這里就是獲取的rpc調用的代理類,grpc給我們提供的就是stub這個,本質是一個東西HelloServiceGrpc.HelloServiceBlockingStub helloService = HelloServiceGrpc.newBlockingStub(managedChannel);//3. 完成RPC調用//3.1 準備參數HelloProto.HelloRequest.Builder builder = HelloProto.HelloRequest.newBuilder();builder.setName("hello");HelloProto.HelloRequest helloRequest = builder.build();//3.1 進行功能rpc調用,獲取相應的內容,像本地調用那樣調用遠程服務HelloProto.HelloResponse helloResponse = helloService.hello(helloRequest);String result = helloResponse.getResult();System.out.println("result = " + result);} catch (Exception e) {throw new RuntimeException(e);}finally {// 4. 關閉通道managedChannel.shutdown();}}
}

成功返回。
在這里插入圖片描述

四、多值傳遞

我們之前在學習proto語法的時候提到過一個關鍵字repeated關鍵字,我們當時說被這個關鍵字修飾的屬性是一個集合類型的字段,grpc會為它生成集合類型的get set方法,我們來做一個測試。

1、proto編寫

我們重新定義一個proto文件的message和方法。

// 定義proto文件版本號
syntax = "proto3";// 生成一個java類即可
option java_multiple_files = false;
// 生成的java類的包名
option java_package = "com.levi";
// 外部類,這里就是HelloProto,實際開發你可以有多個proto管理不同業務類,然后各自的外部類都可以。比如OrderService就是Order.proto 外部類就是OrderProto
option java_outer_classname = "HelloProto";// 定義請求接口參數
message HelloRequest{string name = 1;
}// 定義接口響應參數
message HelloResponse{string result = 1;
}message ManyHelloRequest{repeated string names = 1;
}message ManyHelloResponse{repeated string result = 1;
}// 定義服務
service HelloService{/* 簡單rpc,參數為HelloRequest類型,返回類型為HelloResponse */rpc hello(HelloRequest) returns (HelloResponse){}/* 服務端流式rpc,參數為ManyHelloRequest類型,返回類型為ManyHelloResponse */rpc manyHello(ManyHelloRequest) returns (ManyHelloResponse){}
}

我們在原來的基礎上添加

message ManyHelloRequest{repeated string names = 1;
}message ManyHelloResponse{repeated string result = 1;
}
和一個
rpc manyHello(ManyHelloRequest) returns (ManyHelloResponse){}

旨在請求多個名字,返回也是多個。我們來編譯結果。
在這里插入圖片描述
我們看到message里面沒問題,生成了我們要的。當然之前的那個也保留了。
在這里插入圖片描述
相應的service里面的也沒問題。我們就來修改一下我們的實現。當然為了保險最好刷新一下server和client的pom,重新引入一下api模塊,idea有時候會抽風。

2、server端改寫

package com.levi.service;import com.google.protobuf.ProtocolStringList;
import com.levi.HelloProto;
import com.levi.HelloServiceGrpc;
import io.grpc.stub.StreamObserver;
import org.slf4j.LoggerFactory;import java.util.ArrayList;
import java.util.List;// 服務端實現類
public class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase {private static final String RES_PREFIX = "server#";@Overridepublic void manyHello(HelloProto.ManyHelloRequest request, StreamObserver<HelloProto.ManyHelloResponse> responseObserver) {//1.接受client的請求參數,我們看到此時就是一個nameList的集合了,因為它被repeated修飾了,當然他的類型是ProtocolStringList,是grpc自己的類型ProtocolStringList requestNamesList = request.getNamesList();//2.業務處理System.out.println("請求參數為:" + requestNamesList);// 給返回值的name都加一個前綴List<String> responseNamesList = new ArrayList<>();for (String requestName : requestNamesList) {responseNamesList.add(RES_PREFIX + requestName);}//3.封裝響應//3.1 創建相應對象的構造者HelloProto.ManyHelloResponse.Builder builder = HelloProto.ManyHelloResponse.newBuilder();//3.2 填充數據,多個值要通過addAllResult,或者是下標的方式添加builder.addAllResult(responseNamesList);
//        for (int i = 0; i < requestNamesList.size(); i++) {
//            builder.setResult(i, requestNamesList.get(i));
//        }//3.3 封裝響應HelloProto.ManyHelloResponse helloResponse = builder.build();// 4. 響應clientresponseObserver.onNext(helloResponse);// 5. 響應完成responseObserver.onCompleted();}/*1. 接受client提交的參數  request.getParameter()2. 業務處理 service+dao 調用對應的業務功能。3. 提供返回值*/@Overridepublic void hello(HelloProto.HelloRequest request, StreamObserver<HelloProto.HelloResponse> responseObserver) {//1.接受client的請求參數String name = request.getName();//2.業務處理System.out.println("name parameter "+name);//3.封裝響應//3.1 創建相應對象的構造者HelloProto.HelloResponse.Builder builder = HelloProto.HelloResponse.newBuilder();//3.2 填充數據builder.setResult("hello method invoke ok");//3.3 封裝響應HelloProto.HelloResponse helloResponse = builder.build();// 4. 響應clientresponseObserver.onNext(helloResponse);// 5. 響應完成responseObserver.onCompleted();}
}

然后服務端不用改,還是暴露注冊出去HelloServiceImpl。

package com.levi;import com.levi.service.HelloServiceImpl;
import io.grpc.Server;
import io.grpc.ServerBuilder;import java.io.IOException;public class GrpcServer1 {public static void main(String[] args) throws IOException, InterruptedException {//1. 綁定端口ServerBuilder<?> serverBuilder = ServerBuilder.forPort(9000);//2. 發布服務serverBuilder.addService(new HelloServiceImpl());//serverBuilder.addService(new UserServiceImpl());//3. 創建服務對象Server server = serverBuilder.build();// 啟動服務server.start();// 阻塞等待server.awaitTermination();;}
}

此時我們需要來修改客戶端代碼。

3、cilent端改寫

package com.levi;import com.google.protobuf.ProtocolStringList;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;import java.util.List;// client通過代理對象完成遠端對象的調用
public class GrpcClient2 {public static void main(String[] args) {//1.創建通信的管道ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost", 9000).usePlaintext().build();//2.獲得代理對象 stub進行調用try {// 我們這里以阻塞的形式調用,也就是一直等返回值回來才往下走HelloServiceGrpc.HelloServiceBlockingStub helloService = HelloServiceGrpc.newBlockingStub(managedChannel);//3. 完成RPC調用//3.1 準備參數HelloProto.ManyHelloRequest.Builder builder = HelloProto.ManyHelloRequest.newBuilder();// 多值參數要這樣添加或者以下標形式builder.addAllNames(List.of("levi","tom","jerry"));HelloProto.ManyHelloRequest helloRequest = builder.build();//3.1 進行功能rpc調用,獲取相應的內容,像本地調用那樣調用遠程服務HelloProto.ManyHelloResponse helloResponse = helloService.manyHello(helloRequest);ProtocolStringList resultList = helloResponse.getResultList();System.out.println("resultList = " + resultList);} catch (Exception e) {throw new RuntimeException(e);}finally {// 4. 關閉通道managedChannel.shutdown();}}
}

我們啟動服務端,然后客戶端去請求。
在這里插入圖片描述
沒問題,這就是多值repeated關鍵字的使用方式。

五、關于服務端響應

我們在服務端響應客戶端的時候用的是一個StreamObserver<HelloProto.ManyHelloResponse> responseObserver這個類給客戶端響應的。我們簡單解釋一下這個操作。

public void manyHello(HelloProto.ManyHelloRequest request, StreamObserver<HelloProto.ManyHelloResponse> responseObserver) {...... 省略無關代碼// 4. 響應client,這里其實就是把數據返回給了客戶端responseObserver.onNext(helloResponse);// 5. 響應完成,這個操作其實是給這個通道設置一個標識,告訴客戶端服務端這邊傳完了,客戶端就會拿到數據開始繼續往下走// 如果沒有這個通知,客戶端會一直緩存服務端的數據不會做解析返回。客戶端也一直阻塞著。客戶端會監聽這個通知事件。responseObserver.onCompleted();}

相應的其實客戶端給服務端也會有類似的操作,因為grpc是雙向流,勢必涉及客戶端給服務端的操作。這個等我們后面再說。

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

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

相關文章

Spring Boot 項目中數據同步之binlog和MQ

在 Spring Boot 項目中&#xff0c;“監聽 binlog” 和 “業務代碼中集成 MQ” 是實現數據同步、事件驅動的兩種主流方法。 簡單來說&#xff0c;這個選擇可以概括為&#xff1a; 監聽 Binlog (如使用 Canal)&#xff1a;像一個數據庫的貼身秘書&#xff0c;它忠實地記錄數據庫…

MySQL 寫入性能優化全攻略(附 GitHub 面試題項目鏈接)

面試中你可能會遇到這樣的問題&#xff1a; &#x1f4ac; “假設你的接口一天收到百萬級請求&#xff0c;MySQL 撐得住嗎&#xff1f;你會怎么優化寫入性能&#xff1f;” 剛開始我也懵過&#xff0c;后來不斷復盤與總結&#xff0c;現在我可以用結構化方式給出一個相對完整的…

用Dynamic chunk去干掉tokenizer?

一般你們下AR模型的時候&#xff0c;都有這個&#xff0c;也就是tokenzier&#xff0c;tokenizer是干啥的&#xff0c;其實就是你的分詞字典不光有specal的token對應的還有實際的對應的分詞對應的代碼&#xff0c;比如&#xff1a;也有tokenzier沒顯示的&#xff0c;比如&#…

Linux系統日志管理入門:journalctl命令完全指南

Linux系統日志管理入門&#xff1a;journalctl命令完全指南前言一、journalctl介紹二、基礎使用&#xff1a;快速上手1. 查看全部日志2. 查看本次啟動的日志3. 按時間篩選日志4. 按服務&#xff08;單元&#xff09;過濾日志三、常用參數與場景四、實戰案例&#xff1a;解決實際…

神經網絡的基本骨架——nn.Module的使用(torch.nn庫)

在 PyTorch 中&#xff0c;nn.Module 是所有神經網絡模塊的基類&#xff0c;用于構建和組織深度學習模型。它提供了一系列工具和功能&#xff0c;使模型的定義、訓練和部署更加高效和靈活。nn Neural Network&#xff08;神經網絡&#xff09;核心作用&#xff1a;模塊化設計&…

靜態住宅IP和節點有什么區別?哪種更適合你的需求?

在跨境電商、社媒運營等業務中&#xff0c;“靜態住宅IP”和“節點”常被混淆使用&#xff0c;但兩者代表網絡架構中不同層級的資源。選錯可能導致賬號風控、業務效率低下。IPdodo將在本篇文章中&#xff0c;從本質、業務場景到選擇策略&#xff0c;為您一文道清兩者之間的區別…

AI編程工具對比:Cursor、GitHub Copilot與Claude Code

文章目錄AI編程工具對比&#xff1a;Cursor、GitHub Copilot與Claude Code一、產品定位與核心架構1.1 Cursor&#xff1a;AI原生IDE的代表1.2 GitHub Copilot&#xff1a;代碼補全的行業標桿1.3 Claude Code&#xff1a;終端Agent的革新者二、核心功能深度對比2.1 代碼生成與理…

車載傳統ECU---MCU軟件架構設計指南

我是穿拖鞋的漢子,魔都中堅持長期主義的汽車電子工程師。 老規矩,分享一段喜歡的文字,避免自己成為高知識低文化的工程師: 做到欲望極簡,了解自己的真實欲望,不受外在潮流的影響,不盲從,不跟風。把自己的精力全部用在自己。一是去掉多余,凡事找規律,基礎是誠信;二是…

SCSAI工業智能體的核心特征

一個覆蓋工業全場景、由多個垂直領域智能體構成的開放生態平臺。每個智能體都是具備獨立能力的 “數字單元”&#xff0c;同時又能通過平臺實現互聯、協作與進化。一、工業智能體平臺的核心定位本質&#xff1a;工業領域的 “智能體操作系統 應用市場”&#xff0c;為各類工業…

Java大視界:Java大數據在智能醫療電子健康檔案數據挖掘與健康服務創新>

> 本文通過完整代碼示例,揭秘如何用Java大數據技術挖掘電子健康檔案價值,實現疾病預測、個性化健康管理等創新服務。 ### 一、智能醫療時代的數據金礦 電子健康檔案(EHR)作為醫療數字化的核心載體,包含海量患者全生命周期健康數據。據統計,全球醫療數據量正以每年…

家庭KTV v1.1.9 | 曲庫豐富,無限制免費K歌

家庭KTV是一款專門為家庭娛樂設計的K歌軟件&#xff0c;旨在讓用戶在家中也能享受到與KTV相同的高質量K歌體驗。該應用擁有豐富的歌曲庫&#xff0c;包括經典老歌和當下流行熱曲等多種類型&#xff0c;滿足不同年齡段用戶的需求。家庭KTV支持多種點歌方式&#xff0c;如按照歌手…

vscode 一直連不上遠程,網絡是通的,ssh 也能直接登錄遠程

vscode 一直連不上遠程&#xff0c;網絡是通的&#xff0c;ssh 也能直接登錄遠程&#xff0c;但vscode 死活連不上 解決辦法&#xff1a; 取消勾選remote.SSH.useExecServer 打開 VS Code 設置&#xff08;Ctrl, → 搜索 useExecServer&#xff09; 取消對應的勾選即可

前端面試專欄-工程化:28.團隊協作與版本控制(Git)

&#x1f525; 歡迎來到前端面試通關指南專欄&#xff01;從js精講到框架到實戰&#xff0c;漸進系統化學習&#xff0c;堅持解鎖新技能&#xff0c;祝你輕松拿下心儀offer。 前端面試通關指南專欄主頁 前端面試專欄規劃詳情 項目實戰與工程化模塊-團隊協作與版本控制&#xff…

無標記點動捕:如何突破傳統娛樂邊界,打造沉浸式交互體驗

你能想象在游戲交互中&#xff0c;你的動作和表情可以不用佩戴任何設備就實時映射在虛擬角色上嗎&#xff1f;在傳統娛樂中&#xff0c;用戶體驗常被設備束縛——手柄、傳感器、標記點讓用戶無法徹底投入。而無標記點動捕技術作為一種將用戶肢體轉化為虛擬世界的“自然控制器”…

C#監聽txt文檔獲取新數據

目錄前言一、監聽txt文檔增加數據二、其他功能1. 設置開機自啟動2. 禁止控制臺窗口關閉按鈕3. 阻止Ctrl C中斷4. 防止程序退出&#xff08;無限循環&#xff09;總結前言 之前有個需求就是監聽文件夾中最新的txt文檔獲取最新數據&#xff0c;還有其他功能&#xff0c;比如&am…

程序員管理與AIStarter開發:如何避免需求Bug,提升項目效率

大家好&#xff0c;我是熊哥&#xff01;今天聊聊程序員管理和AIStarter開發中的經驗教訓。創業公司項目常因需求不清出Bug&#xff0c;比如“管理員刪管理員”這種低級錯誤&#xff0c;引發用戶不滿。熊哥親測&#xff1a;程序員管理關鍵在于明確需求&#xff01;通過整理需求…

網絡爬蟲概念初解

大家好! 網絡爬蟲&#xff08;Web Crawler&#xff09;是一種自動化程序&#xff0c;能夠模擬人類瀏覽行為&#xff0c;按照預設規則從互聯網上抓取、解析和存儲數據。它像一只“數字蜘蛛”&#xff0c;沿著網頁鏈接爬行&#xff0c;高效采集目標信息。以下是核心要點&#xff…

Pytorch 使用報錯 RuntimeError: Caught RuntimeError in DataLoader worker process 0.

這個錯誤是可能是由于在DataLoader的工作進程中嘗試訪問CUDA設備導致的。PyTorch的DataLoader使用多進程加載數據&#xff0c;而CUDA上下文不能在子進程中直接使用。修改前的代碼為&#xff1a;def prepare_data(file_path):# 讀取Excel文件df pd.read_excel(file_path, heade…

產品經理如何描述用戶故事

作為資深產品經理&#xff0c;描述用戶故事需超越基礎模板&#xff0c;將其轉化為驅動產品決策的戰略工具。以下是融合實戰經驗的深度方法論&#xff0c;附高階技巧和反例分析&#xff1a;一、用戶故事的本質&#xff1a;需求的三維錨點 #mermaid-svg-AgAM5YJT6aKoD1EV {font-f…

Vue 結合 Zabbix API 獲取服務器 CPU、內存、GPU 等數據

一、簡介 Vue 結合 Zabbix API 可以實現對服務器 CPU、內存、GPU 等監控數據的動態獲取與展示。Zabbix 是一款開源的監控工具&#xff0c;提供豐富的 API 接口供開發者調用。通過 Vue 前端框架&#xff0c;可以將 Zabbix 返回的數據以圖表或表格形式直觀呈現&#xff0c;便于運…