做者:亞瑟、文遠java
一個小巧的單體應用會隨着公司業務的擴張而慢慢成長,逐漸演化成一個龐大且複雜的系統怪物,系統任何一處的問題都將影響整個怪物的表現,不多有單獨的開發者能理清系統怪物全部的肌理脈絡,致使bug的定位和新功能的擴展都變得愈來愈困難,對系統的任一改動都要求整個怪物一塊兒迴歸測試並從新部署,效率必然不高。因此公司發展到了必定階段,總會須要從架構上尋找解決系統怪物之道,而微服務就是目前最流行的架構方案之一,它將系統怪物拆分紅多個獨立自治的服務小怪獸,讓咱們有能力分而治之。spring
插畫:牛肉bash
一旦系統怪物被拆分紅了多個服務小怪獸,小怪獸們如何溝通協做就成了咱們最關心的問題。服務小怪獸間的通訊就好像發電報同樣,涉及到數據序列化、反序列化、鏈接管理、收發線程、超時處理等多個問題,RPC框架的出現解決了這些問題,就好像經過電報員發電報同樣,使用RPC框架讓小怪獸們沒必要關心通訊的底層細節。網絡
插畫:牛肉架構
若是通訊的小怪獸們語言不通,那麼咱們須要對電報員(亦即RPC框架)的人選提出更高的要求,不管小怪獸們用的是什麼語言,協助通訊的兩位電報員都必須把它們的話翻譯成電報員彼此能理解的同一種語言,亦即IDL(Interface Description Language),是的,電報員在這種狀況下還必須承擔翻譯的角色,而gRPC就是一位如此優秀的電報員。併發
插畫:牛肉框架
實現Node客戶端小怪獸發送"今晚的月色真美",Java服務端小怪獸收到電報內容,並回復"I love you too"。jvm
經過Spring Boot建立Java項目,pom.xml中加入以下依賴maven
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-netty-shaded</artifactId> <version>1.21.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-protobuf</artifactId> <version>1.21.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-stub</artifactId> <version>1.21.0</version> </dependency> </dependencies> <build> <extensions> <extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>1.5.0.Final</version> </extension> </extensions> <plugins> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.5.1</version> <configuration> <protocArtifact>com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier}</protocArtifact> <pluginId>grpc-java</pluginId> <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.21.0:exe:${os.detected.classifier}</pluginArtifact> <!--指定生成文件目錄--> <outputDirectory>src/main/java</outputDirectory> <!--從新生成文件時不清除 原有src/main/java下的內容--> <clearOutputDirectory>false</clearOutputDirectory> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
定義IDL文件tcp
syntax = "proto3"; option java_multiple_files = true; option java_package = "net.changjinglu.proto"; option java_outer_classname = "TelegraphProto"; package telegraph; // The greeting service definition. service TelegraphService { // Sends a greeting rpc SayLove (LoveRequest) returns (LoveReply) {} } // The request message containing the user's name. message LoveRequest { string message = 1; } // The response message containing the greetings message LoveReply { string message = 1; }
編譯生成IDL定義的Java服務接口,相關代碼會生成到配置對應的路徑下
mvn clean install
實現IDL定義的Java服務接口
public class TelegraphGreeterImpl extends TelegraphServiceGrpc.TelegraphServiceImplBase { @Override public void sayLove(LoveRequest request, StreamObserver<LoveReply> responseObserver) { System.out.println("收到Node小怪獸的消息:"+request.getMessage()); responseObserver.onNext(LoveReply.newBuilder().setMessage("I Love U Too").build()); //結束 responseObserver.onCompleted(); } }
編寫並啓動Java服務端
public class GrpcServer { /** GRPC 服務端 */ private Server server; public static void main(String[] args) throws IOException, InterruptedException { GrpcServer grpcService = new GrpcServer(); grpcService.start(); System.out.println("GRPC 服務端啓動成功"); //GRPC 服務端須要手動阻塞線程 grpcService.waitTermination(); } private void start() throws IOException { //綁定接口、啓動服務 this.server = ServerBuilder.forPort(8899) .addService(new TelegraphGreeterImpl()) .build() .start(); System.out.println("server start!"); //這裏是爲了防止jvm關閉了,可是tcp尚未關閉的狀況 Runtime.getRuntime().addShutdownHook(new Thread(()->{ System.out.println("關閉jvm"); GrpcServer.this.stop(); })); } private void stop() { if (this.server != null) { this.server.shutdown(); } } private void waitTermination() throws InterruptedException { if (this.server != null) { server.awaitTermination(); } } }
編寫並啓動Nodejs客戶端,客戶端使用相同的IDL
var PROTO_FILE_PATH = '/Users/wenyuan/Nodejs/grpc/proto/telegraph.proto'; var grpc = require('grpc'); var grpcService = grpc.load(PROTO_FILE_PATH).telegraph; function main() { var stub = new grpcService.TelegraphService('localhost:8899',grpc.credentials.createInsecure()); stub.sayLove({message:'今晚的月色真美'},function (error, result) { console.log('收到Java小怪獸的消息: ' + result.message); }); } main();