开始食用grpc(之一)
開(kāi)始食用grpc(之一)
轉(zhuǎn)載請(qǐng)注明出處:https://www.cnblogs.com/funnyzpc/p/9501353.html
?
```
? 記一次和一鍋們壓馬路,路過(guò)一咖啡廳(某巴克),隨口就問(wèn)隨行的鍋門(mén):你能從那咖啡廳看到什么?
? ? ?當(dāng)時(shí)的那家某巴克處于鬧市,也正值周末,屋外屋內(nèi)喝咖啡的人幾近乎十分的安靜,使用電腦的,刷手機(jī)的、做作業(yè)的。。。而且大都是年輕人和中年人。
? 鍋門(mén)撂了句:一群屌絲唄 (;?_?)?
。。。白了他一眼(¬_¬)
? ? ?( ...其實(shí)想教唆他進(jìn)去看看美女,歇歇腳來(lái)著??乛?乛??)
.......
? ? ?許久之后,也就是最近看到詩(shī)人余秀華的一句話后忽有所感,原句是:
“反正是背負(fù)慢慢凋殘的孤獨(dú),耀眼的孤獨(dú),義無(wú)反顧的孤獨(dú)”
這才明白他們是在消費(fèi)孤獨(dú),也為孤獨(dú)所消費(fèi); 他們是,周?chē)娜耸?#xff0c;還有?( ?? .? ?? )
那~ 孤獨(dú)的結(jié)果是什么呢 ?
```
這次講下大系統(tǒng)通訊必備的一項(xiàng)組件:rpc,rpc有很多 如 dubbo、thirft、feign、motan、grpc 等~,這其中有字符串方式的也有二進(jìn)制流方式的;整體來(lái)說(shuō)二進(jìn)制方式的一般會(huì)較字符串方式的快許多,字符形式的的慢,但是簡(jiǎn)單;而二進(jìn)制方式的 序列化和跨平臺(tái)較為麻煩些;我個(gè)人選取rpc時(shí)一般綜合考慮一下幾點(diǎn):
A>傳輸方式是否是二進(jìn)制
B>是否長(zhǎng)期支持
C>是否支持跨平臺(tái),開(kāi)源組件是否豐富
C+>是否支持異步調(diào)用
D>有無(wú)明顯的bug或缺點(diǎn)
E>維護(hù)和開(kāi)發(fā)是否有好
? ? 綜合下來(lái),個(gè)人堅(jiān)定之選擇grpc,這個(gè)初出茅廬(2015年發(fā)布)的東東,十分強(qiáng)大:
? ? ? >> http2流方式
>>?自帶異步特性
>>?跨平臺(tái)(支持11種語(yǔ)言)
>>?無(wú)需zookeeper這類單獨(dú)的服務(wù)中心
>>?對(duì)開(kāi)發(fā)人員比較友好
? ? ? >> 服務(wù)調(diào)用方可設(shè)置請(qǐng)求頭
當(dāng)然缺點(diǎn)也是存在的:需要單獨(dú)寫(xiě)proto文件(生成目標(biāo)語(yǔ)言的一套語(yǔ)法定義)、變量為null時(shí)會(huì)賦予默認(rèn)初始值、鏈?zhǔn)秸{(diào)用(還好調(diào)用接口較為單一,只是語(yǔ)法較為怪異)...
如果您在意以上缺點(diǎn),可繞過(guò)本文哈~
? ? ? ok,現(xiàn)在開(kāi)始我開(kāi)始講grpc,內(nèi)容大致有四:
A->grpc的簡(jiǎn)單配置 (本節(jié))
A>簡(jiǎn)單grpc編寫(xiě)?(本節(jié))
B>復(fù)雜grpc proto服務(wù)文件編寫(xiě)?(本節(jié))
C>雙向流式調(diào)用方法及注意事項(xiàng) (下一節(jié))
D>grpc安全問(wèn)題及攔截器 (下一節(jié))
?
grpc的配置:
? ? ? ?這里我的工程是基于springboot,同時(shí)為簡(jiǎn)化開(kāi)發(fā)起見(jiàn),我使用?grpc-spring-boot-starter?,開(kāi)始之前先感謝這位開(kāi)發(fā)者為簡(jiǎn)化grpc的java平臺(tái)簡(jiǎn)化了太多的開(kāi)發(fā),同時(shí)也為springcloud融合做了太多共享,非常感謝~!?
這里,首先得準(zhǔn)備三個(gè)springboot模塊,這三個(gè)模塊包含:grpc proto3文件生成模塊、grpc 客戶端、grpc 服務(wù)端,我的工程結(jié)構(gòu)大致是這樣子的(工程是多模塊的):
這里面的三個(gè)模塊一看就懂,就不細(xì)講啦~,準(zhǔn)備好這三個(gè)模塊后,依次配置依賴包及參數(shù):
服務(wù)端(preview-grpc-server):
pom.xml中依賴包配置>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId></dependency><dependency><groupId>net.devh</groupId><artifactId>grpc-client-spring-boot-autoconfigure</artifactId><version>RELEASE</version></dependency><!-- 由于我的工程是多模塊的,若不作為jar包引入,也可以將preview-grpc-lib中的java文件拷貝到當(dāng)前工程內(nèi)也可 -->
<dependency>
<groupId>com.github.carvechris</groupId>
<artifactId>preview-grpc-lib</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency> ?
配置文件yml(如果是properties文件也可參照此配置):
1 grpc: 2 server: 3 port: 2804 4 5 spring: 6 application: 7 name: preview-grpc-server?
(注意:一定要定義應(yīng)用名稱,在調(diào)用的時(shí)候會(huì)用到應(yīng)用名稱的,這里是:preview-grpc-server)
?客戶端(preview-grpc-client):
pom.xml文件依賴包配置>
<dependency><groupId>net.devh</groupId><artifactId>grpc-client-spring-boot-starter</artifactId><version>1.4.0.RELEASE</version> </dependency><!-- 由于我的工程是多模塊的,若不作為jar包引入,也可以將preview-grpc-lib中的java文件拷貝到當(dāng)前工程內(nèi)也可 --> <dependency>
<groupId>com.github.carvechris</groupId>
<artifactId>preview-grpc-lib</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency> ?
?
yml配置文件參數(shù):
1 grpc: 2 client: 3 preview-grpc-server: 4 host: 5 - 127.0.0.1 6 port: 7 - 2804 8 enableKeepAlive: true 9 keepAliveWithoutCalls: true?
proto文件生成模塊(preview-grpc-lib)配置:
pom.xml文件依賴包配置:
1 <!--依賴配置--> 2 <dependencies> 3 <dependency> 4 <groupId>io.grpc</groupId> 5 <artifactId>grpc-netty</artifactId> 6 <version>${grpc.version}</version> 7 </dependency> 8 <dependency> 9 <groupId>io.grpc</groupId> 10 <artifactId>grpc-protobuf</artifactId> 11 <version>${grpc.version}</version> 12 </dependency> 13 <dependency> 14 <groupId>io.grpc</groupId> 15 <artifactId>grpc-stub</artifactId> 16 <version>${grpc.version}</version> 17 </dependency> 18 </dependencies> 19 20 <!--proto3文件生成java代碼插件配置--> 21 <build> 22 <extensions> 23 <extension> 24 <groupId>kr.motd.maven</groupId> 25 <artifactId>os-maven-plugin</artifactId> 26 <version>${os.plugin.version}</version> 27 </extension> 28 </extensions> 29 <plugins> 30 <plugin> 31 <groupId>org.xolstice.maven.plugins</groupId> 32 <artifactId>protobuf-maven-plugin</artifactId> 33 <version>${protobuf.plugin.version}</version> 34 <configuration> 35 <protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}</protocArtifact> 36 <pluginId>grpc-java</pluginId> 37 <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact> 38 </configuration> 39 </plugin> 40 </plugins> 41 </build>?
配置完成,這里需要特別說(shuō)明一下:server模塊和client模塊的web服務(wù)各占一個(gè)端口,另外,server模塊還會(huì)給grpc單獨(dú)分配一個(gè)端口,就我的preview-grpc-server來(lái)說(shuō):
服務(wù)名稱(name)是:preview-grpc-server
服務(wù)占用的端口是:2804
切記,不論是web服務(wù)還是grpc服務(wù)的端口都不能重復(fù),同時(shí)一定要理清楚web服務(wù)和grpc服務(wù)所占用的端口和ip。
?
簡(jiǎn)單grpc服務(wù)(helloworld.proto)編寫(xiě):
這里我先展示下我的生成模塊的大致樣子:
?
需要說(shuō)明的是:編寫(xiě)的proto文件均在proto目錄下,java目錄下是proto文件生成的java代碼,這里的java文件是從target目錄總復(fù)制到j(luò)ava目錄下的,包名一定要與proto里面聲明的包名一致!
java代碼生成模塊proto3服務(wù)文件(helloworld.proto)的編寫(xiě):
1 syntax = "proto3"; 2 3 // 是否拆分類文件 4 option java_multiple_files = true; 5 // 生成的文件所在的包 6 option java_package = "com.funnyzpc.xxx.grpc.lib.hello"; 7 // 輸出類主文件(此配置可選) 8 option java_outer_classname = "HelloWorldProto"; 9 10 // 定義一個(gè)服務(wù) 11 service Simple { 12 // Sends a greeting 13 rpc SayHello (HelloRequest) returns (HelloReply) { 14 } 15 } 16 17 // 請(qǐng)求體定義 18 message HelloRequest { 19 string name = 1; 20 } 21 22 // 響應(yīng)體定義 23 message HelloReply { 24 string message = 1; 25 }現(xiàn)在開(kāi)始使用idea提供的快捷功能生成客戶端和服務(wù)端java文件(當(dāng)然也可以使用mvn命令手動(dòng)生成):
將生成的java文件復(fù)制到應(yīng)用目錄:
(注意:復(fù)制后一定要清理target目錄,不然文件重復(fù)會(huì)報(bào)錯(cuò)!)
在客戶端(preview-grpc-client)編寫(xiě)一個(gè)grpc服務(wù)請(qǐng)求類(GrpcSimpleService.java):
1 @Service 2 public class GrpcSimpleService { 3 4 @GrpcClient("preview-grpc-server") 5 private Channel serverChannel; 6 7 public String sendMessage(String name) { 8 SimpleGrpc.SimpleBlockingStub stub = SimpleGrpc.newBlockingStub(serverChannel); 9 HelloReply response = stub.sayHello(HelloRequest.newBuilder().setName(name).build()); 10 return response.getMessage(); 11 } 12 }在服務(wù)端(preview-grpc-server)編寫(xiě)對(duì)應(yīng)的grpc的服務(wù)類:
1 /** 2 * 簡(jiǎn)單grpc服務(wù)類 3 */ 4 @GrpcService(SimpleGrpc.class) 5 public class GrpcServerService extends SimpleGrpc.SimpleImplBase { 6 private static final Logger LOG=LoggerFactory.getLogger(GrpcServerService.class); 7 8 @Override 9 public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) { 10 HelloReply reply = HelloReply.newBuilder().setMessage("Hello ===> " + req.getName()).build(); 11 responseObserver.onNext(reply); 12 responseObserver.onCompleted(); 13 } 14 15 }上面的@GrpcService是grpc組件的注解,注解中必須聲明所使用的grpc(生成的類中的)服務(wù)類,同時(shí)還可以聲明所使用的攔截器(可選)
OK,現(xiàn)在添加一個(gè)控制器(在preview-grpc-client中編寫(xiě)一個(gè)控制器),試試看:
完美,。。。可能有人proto文件一知半解,接下來(lái)進(jìn)入下一節(jié)。
?
復(fù)雜grpc proto服務(wù)文件編寫(xiě):
首先,我先推薦兩個(gè)官方網(wǎng)站,若能理解官網(wǎng)內(nèi)容,可繞過(guò)本節(jié)
? ?grpc java平臺(tái)api及樣例>
https://grpc.io/docs/quickstart/java.html
? ? ? ? ?protocol buffers,proto3文件語(yǔ)法>
https://developers.google.com/protocol-buffers/docs/proto3?hl=zh-cn
一般在跨應(yīng)用調(diào)用時(shí),所傳遞的參數(shù)有時(shí)候?yàn)閺?fù)雜對(duì)象,比如這樣{page_no:1,page_size:20,data:{name:XXX,type:2}},這里就寫(xiě)好的復(fù)雜層級(jí)對(duì)象講解下:
(MultiObject.proto)
1 syntax = "proto3"; 2 3 // option java_multiple_files = true; 4 option java_package = "com.github.carvechris.security.grpc.lib.multi"; 5 6 service MultiObjService{ 7 rpc queryObj (MultiObjReq) returns (MultiObjResp) { 8 9 } 10 11 } 12 13 message MultiObjReq{ 14 int32 page_no=1; 15 int32 page_size=2; 16 MultiObjDataReq data=3; 17 } 18 19 message MultiObjDataReq{ 20 string name=1; 21 int32 type=2; 22 } 23 24 message MultiObjResp{ 25 string req_str=1; 26 MultiObjFirstResp first=2; 27 } 28 message MultiObjFirstResp{ 29 string f_content=1; 30 MultiObjNextResp next=2; 31 32 } 33 message MultiObjNextResp{ 34 string n_content =1; 35 }文件中定義一個(gè)服務(wù) 必須以關(guān)鍵字 service 開(kāi)始,上面的?MultiObjReq 與 MultiObjResp 分別為服務(wù)的請(qǐng)求和響應(yīng)對(duì)象,這兩個(gè)對(duì)象在生成java文件后,每個(gè)請(qǐng)求對(duì)象都是一個(gè)單獨(dú)的類(可在一個(gè)java中也可不在,若不在需要定義:option java_multiple_files = true;),不管是請(qǐng)求對(duì)象還是響應(yīng)對(duì)象,都需要單獨(dú)聲明這個(gè)對(duì)象以及對(duì)象中的變量類型及所處的位置,就像這樣:
1 message MultiObjReq{ 2 int32 page_no=1; 3 int32 page_size=2; 4 MultiObjDataReq data=3; 5 }自定義類型需要在單獨(dú)定義,比如"MultiObjDataReq";在上面這個(gè)例子中,定義的請(qǐng)求對(duì)象MultiObjReq的第一個(gè)字段為 page_no,第一個(gè)為page_size,第三個(gè)為定義的一個(gè)對(duì)象,每個(gè)參數(shù)開(kāi)始需標(biāo)明當(dāng)前字段類型,如果這個(gè)字段是自定義類型時(shí)無(wú)需定義數(shù)據(jù)類型(但是一定要有參數(shù)序號(hào),當(dāng)然也可以定義一個(gè)map類型);另外,通用字段類型同go語(yǔ)言的數(shù)據(jù)類型(參照以上鏈接);注意,請(qǐng)求或響應(yīng)對(duì)象定義時(shí)必須以關(guān)鍵字message開(kāi)始。
另外,請(qǐng)注意,如果某個(gè)字段是個(gè)列表(java中的List),需要在字段或者對(duì)象前添加關(guān)鍵字 repeated ,這樣:
//返回體數(shù)據(jù)定義 message GrpcResp {string sign=3;string msg=4;repeated datas detail=5; }message datas{uint64 id=1;string address=2;double isadmin=3; }現(xiàn)在展示上面的MultiObject.proto文件 所編寫(xiě)的客戶端和服務(wù)端類:
客戶端(preview-grpc-client):
(GrpcMultiObjClientService.java)
1 @Service 2 public class GrpcMultiObjClientService { 3 4 @GrpcClient("preview-grpc-service") 5 private Channel serverChannel; 6 7 public Object testMultiObj() { 8 9 MultiObject.MultiObjDataReq reqData=MultiObject.MultiObjDataReq.newBuilder() 10 .setName("queryName") 11 .setType(33) 12 .build(); 13 14 MultiObject.MultiObjReq req=MultiObject.MultiObjReq.newBuilder() 15 .setPageNo(11) 16 .setPageSize(22) 17 .setData(reqData) 18 .build(); 19 20 MultiObjServiceGrpc.MultiObjServiceBlockingStub stb=MultiObjServiceGrpc.newBlockingStub(serverChannel); 21 MultiObject.MultiObjResp resp=stb.queryObj(req); 22 23 Map<String,Object> reMap=new HashMap<>(); 24 reMap.put("getFContent",resp.getFirst().getFContent()); 25 reMap.put("getNContent",resp.getFirst().getNext().getNContent()); 26 reMap.put("getReqStr",resp.getReqStr()); 27 return reMap; 28 } 29 30 }服務(wù)端(preview-grpc-server):
(GrpcMultiObjService.java)
1 @GrpcService(MultiObjServiceGrpc.class) 2 public class GrpcMultiObjService extends MultiObjServiceGrpc.MultiObjServiceImplBase{ 3 private static final Logger LOG=LoggerFactory.getLogger(GrpcMultiObjService.class); 4 5 @Override 6 public void queryObj(MultiObject.MultiObjReq request,StreamObserver<MultiObject.MultiObjResp> responseObserver) { 7 8 LOG.info("MultiObjServiceGrpc>start"); 9 Map<String,Object> reqData=new HashMap<String,Object>(); 10 reqData.put("getPageNo",request.getPageNo()); 11 reqData.put("getPageSize",request.getPageSize()); 12 reqData.put("getName",request.getData().getName()); 13 reqData.put("getType",request.getData().getType()); 14 15 MultiObject.MultiObjNextResp next=MultiObject.MultiObjNextResp.newBuilder().setNContent("n_content").build(); 16 MultiObject.MultiObjFirstResp first=MultiObject.MultiObjFirstResp.newBuilder().setFContent("f_content").setNext(next).build(); 17 MultiObject.MultiObjResp resp=MultiObject.MultiObjResp.newBuilder().setReqStr(JSON.toJSONString(reqData)).setFirst(first).build(); 18 19 responseObserver.onNext(resp); 20 responseObserver.onCompleted(); 21 LOG.info("MultiObjServiceGrpc>end"); 22 } 23 24 }?
現(xiàn)在是 2018-08-26?02:13:42?
由于在雙向流編寫(xiě)及測(cè)試環(huán)節(jié)碰到些問(wèn)題,耽擱了許久,此次會(huì)將雙向流和安全及攔截器放在下一篇講,各位,晚安 (=。=)
轉(zhuǎn)載于:https://www.cnblogs.com/funnyzpc/p/9501353.html
總結(jié)
以上是生活随笔為你收集整理的开始食用grpc(之一)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【SSH异常】InvalidDataAc
- 下一篇: [SCOI2014]方伯伯的OJ