開始食用grpc(之二)

開始食用grpc(之二)html

轉載請註明出處:http://www.javashuo.com/article/p-ppirkdpu-e.htmljava

```安全

  前段時間有童鞋找我開專欄、搬家、甚至還有人找我寫書的。。。這其中有大平臺 疼訊、阿里...,也有小平臺 :第八基地、雲聚、西部數碼...,在此再次感謝各位賞識,吾文采拙劣,技術水平較次,實在沒時間寫書,也沒時間給各位解答不熟悉的技術問題...;同時邀請我開專欄、搬家的平臺也請不要重複邀請呢。服務器

  額,同時對於轉載的童鞋,須要說明的是:個人博客是免費公益性質的,若能受到點兒啓發或者有些許的幫助就是對比人最大的確定,基於此,請各位轉載的童鞋 能 原文轉載(對原做者極大的尊重),如果平臺轉載請在轉載的後的博客頁面中切勿投放過多的廣告,在此我強調過個人博客是免費性質的,如果拿來作付費或是賺取廣告費性質的請和我聯繫(能夠允許少量的廣告,廣告也不可遮蓋博客內容),在此請各位諒解哈~,同時感謝深耕在開源一線的童鞋,是大家改善了一線開發人員的處境,也讓整個行業變得快速高效,致敬!app

```異步

  這次我就接着上次的話茬把我所瞭解的grpc將完吧,grpc這兩節的內容大體以下:ide

    A->grpc的簡單配置 (上一節)測試

    A>簡單grpc編寫 (上一節)ui

    B>複雜grpc proto服務文件編寫 (上一節)spa

    C>雙向流式調用方法及注意事項 (本節)

    D>grpc安全問題及攔截器 (本節)

  此次我是這麼安排的,先列舉一個雙向流的編寫過程,而後在講講這裏面的坑,而後再淺談一下grpc安全問題,同時編寫一個簡單的grpc攔截器,若基本配置不是很清楚請仔細閱讀 http://www.javashuo.com/article/p-cmuaepsn-e.html

 

雙向流式調用方法及注意事項:

  因爲雙向流的使用方式不用於上期所講的,這裏我從編寫一步步講。

  先在preview-grpc-lib工程先的proto文件夾下編寫一個包含雙向流的是proto文件以生成客戶端和服務器相關代碼(記得把生成的代碼放入工程內)。

  (MultiStream.proto)

 1 syntax = "proto3";
 2 
 3 option java_multiple_files = true;
 4 option java_package = "com.funnyzpc.XXX.grpc.lib.multiStream";
 5 //定義一個服務
 6 service MultiStreamService{
 7     rpc queryStream (stream MultiStreamReq) returns (stream MultiStreamResp) {
 8 
 9     }
10 
11 }
12 //定義一個請求體(用於傳參)
13 message MultiStreamReq{
14     int32 page_no=1;
15     int32 page_size=2;
16     MultiStreamDataReq data=3;
17 }
18 
19 message MultiStreamDataReq{
20     string name=1;
21     bool type=2;
22 }
23 //定義一個響應體(用於回參)
24 message MultiStreamResp{
25     string req_str=1;
26     MultiStreamFirstResp first=2;
27 }
28 message MultiStreamFirstResp{
29     string f_content=1;
30     int64 idx=2;
31 
32 }

這裏可能須要對比着上一節所講的複雜proto文件編寫的內容,能夠看到:請求體和響應體的定義大體都是同樣的,只是在服務定義的時候會有一些些差異>請求體和響應體的前面多了一個關鍵字"stream」 ,就是(請求或響應)只要一方是以流的方式發送就須要聲明爲 「stream" 。

  編寫個客戶端服務代碼:

 1 @Service
 2 public class GrpcMultiStreamClientService {
 3     private static final Logger LOG=LoggerFactory.getLogger(GrpcMultiStreamClientService.class);
 4 
 5     @GrpcClient("preview-grpc-server")
 6     private Channel rpcChannel;
 7 
 8     /**
 9      * grpc>雙向流方式
10      * @return
11      */
12     public Object queryByStream()throws Exception{
13         Map<String,Object> resp=new HashMap<>();
14 
15         StreamObserver<MultiStreamResp> req= new StreamObserver<MultiStreamResp>() {
16             @Override
17             public void onNext(MultiStreamResp multiStreamResp) {
18                 resp.put("req_str",multiStreamResp.getReqStr());
19                 resp.put("f_content",multiStreamResp.getFirst().getFContent());
20                 resp.put("idx",multiStreamResp.getFirst().getIdx());
21                 LOG.info("onNext()");
22                 //return resp;
23             }
24 
25             @Override
26             public void onError(Throwable throwable) {
27                 LOG.info("onError()");
28             }
29 
30             @Override
31             public void onCompleted() {
32                 LOG.info("onCompleted()");
33             }
34         };
35 
36         MultiStreamServiceGrpc.MultiStreamServiceStub stud=MultiStreamServiceGrpc.newStub(rpcChannel);
37         StreamObserver<MultiStreamReq> reqStream=stud.queryStream(req);
38 
39         MultiStreamDataReq streamDataReq=MultiStreamDataReq.newBuilder()
40                 .setName("req>name field")
41                 .setType(false)
42                 .build();
43         MultiStreamReq streamReq= MultiStreamReq.newBuilder()
44                 .setPageNo(1)
45                 .setPageSize(20)
46                 .setData(streamDataReq).build();
47 
48         reqStream.onNext(streamReq);
49         reqStream.onCompleted();
50         return resp;
51     }
52 }

能夠在上圖看到,請求方法內首先是要放入一個構造的內部請求方法,請求體也須要放入到StreamObserver這個對象中,這是與以前編寫的grpc客戶端(阻塞)所不同的地方,同時構造stub的時候是newStub而不是newBlockingStub ,固然這二者是有區別的,前者僅適用於http2二進制流的方式 而且是一個異步的(這是重點)然後者是僅適用於http1.1的字符明文方式 而且是阻塞方式(這也是重點),後面我會說說這二者的具體使用區別。

  接下來寫一個grpc服務端服務類,這是代碼

 1 @GrpcService(value= MultiStreamServiceGrpc.class)
 2 public class GrpcMultiStreamService extends MultiStreamServiceGrpc.MultiStreamServiceImplBase{
 3     private static final Logger LOG= LoggerFactory.getLogger(GrpcMultiStreamService.class);
 4 
 5     @Override
 6     public StreamObserver<MultiStreamReq> queryStream(StreamObserver<MultiStreamResp> resp) {
 7         return new StreamObserver<MultiStreamReq>() {
 8             @Override
 9             public void onNext(MultiStreamReq multiStreamReq) {
10                 MultiStreamFirstResp firstResp=MultiStreamFirstResp.newBuilder()
11                         .setFContent("f_content")
12                         .setIdx(99).build();
13                 MultiStreamResp req=MultiStreamResp.newBuilder()
14                         .setReqStr("req_str")
15                         .setFirst(firstResp).build();
16                 resp.onNext(req);
17                 resp.onCompleted();
18             }
19 
20             @Override
21             public void onError(Throwable throwable) {
22                 LOG.info("onError()");
23             }
24 
25             @Override
26             public void onCompleted() {
27                 LOG.info("onCompleted()");
28             }
29         };
30 31 32 }

總體的構造方法和邏輯代碼和客戶端代碼類似,同時服務端的邏輯代碼基本上全在StreamObserver這個異步對象中處理,同時這個構造方法也提供了錯誤和完成所對的重載方法,要進行業務處理也必須在重載的onNext方法中編寫。

   主題的服務已經編寫完成,如今添加一個控制器來看看這個服務有沒問題

1     @Autowired
2     private GrpcMultiStreamClientService multiStreamClientService;
3 
4     @RequestMapping("/grpc4")
5     public Object grpc4()throws Exception{
6         return multiStreamClientService.queryByStream();
7     }

 

可能你會咦的一聲說:請求是成功的,但爲何取到的服務端的參數是空呢?

其實這個很好理解,由於客戶端的請求服務方式是流,此種方式下響應固然是異步的,這裏方便調試,須要添加線程阻塞,纔可能獲取到服務端的響應參數(下圖中紅色部分)>

 1 @Service
 2 public class GrpcMultiStreamClientService {
 3     private static final Logger LOG=LoggerFactory.getLogger(GrpcMultiStreamClientService.class);
 4 
 5     @GrpcClient("preview-grpc-server")
 6     private Channel rpcChannel;
 7 
 8     /**
 9      * grpc>雙向流方式
10      * @return
11      */
12     public Object queryByStream()throws Exception{
13         Map<String,Object> resp=new HashMap<>();
14 
15         StreamObserver<MultiStreamResp> req= new StreamObserver<MultiStreamResp>() {
16             @Override
17             public void onNext(MultiStreamResp multiStreamResp) {
18                 resp.put("req_str",multiStreamResp.getReqStr());
19                 resp.put("f_content",multiStreamResp.getFirst().getFContent());
20                 resp.put("idx",multiStreamResp.getFirst().getIdx());
21                 LOG.info("onNext()");
22                 //return resp;
23             }
24 
25             @Override
26             public void onError(Throwable throwable) {
27                 LOG.info("onError()");
28             }
29 
30             @Override
31             public void onCompleted() {
32                 LOG.info("onCompleted()");
33             }
34         };
35 
36         MultiStreamServiceGrpc.MultiStreamServiceStub stud=MultiStreamServiceGrpc.newStub(rpcChannel);
37         StreamObserver<MultiStreamReq> reqStream=stud.queryStream(req);
38 
39         MultiStreamDataReq streamDataReq=MultiStreamDataReq.newBuilder()
40                 .setName("req>name field")
41                 .setType(false)
42                 .build();
43         MultiStreamReq streamReq= MultiStreamReq.newBuilder()
44                 .setPageNo(1)
45                 .setPageSize(20)
46                 .setData(streamDataReq).build();
47 
48         reqStream.onNext(streamReq);
49         reqStream.onCompleted();
50         Thread.sleep(10000); 51         return resp;
52     }
53 }

能夠看到線程睡眠了10秒,若是打斷點能夠看到 睡眠的過程當中會響應客戶端中的onNext方法,再就是把參數放入到resp中,固然客戶端服務爲流的方式下通常不作線程睡眠的操做,由於服務器有可能超時,若是超時那可就麻煩了。因此說grpc異步是有極好的應用場景,好比業務費阻塞,日誌處理等等,同時若是須要直接響應請使用阻塞的方式(上面已經說過了),好了,這個時候,咱們看看結果>

ok,能夠順利的看到服務器的響應結果了

 

grpc安全問題及攔截器:

  對於grpc安全問題,grpc只在服務端提供了 服務端證書驗證 的方式,具體就是在在客戶端請求的時候驗證客戶地址是不是有效而已,默認不使用的時候服務端證書的開關是關閉着的,這個驗證其實也很簡陋,具體的能夠看看源碼便知

如若開發的系統要保證極高的安全度,建議使用這兩類方式:

  A>將客戶端應用和服務端應用放置在同一個內往下,服務端關閉外網直接訪問

  B>能夠在服務端添加攔截器,使用token的方式來驗證客戶端身份是否合法(這種方式可能須要客戶端設置請求頭)

  對於以上兩種安全訪問方式,也能夠以混合的方式使用,對於以上後者,我簡單的列舉下如何使用攔截器,就一個簡單的例子呵~

  首先填寫一個服務端攔截器>

 1 public class GrpcInterceptor implements ServerInterceptor {
 2     private static final Logger LOG=LoggerFactory.getLogger(GrpcInterceptor.class);
 3 
 4     @Override
 5     public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
 6         LOG.info(call.getAttributes().toString());
 7         String inetSocketString = call.getAttributes()
 8                 .get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR).toString();
 9         LOG.info(inetSocketString);
10         return next.startCall(call,headers);
11     }
12 }

如上,攔截器實現於grpc 的 ServerInterceptor 來編寫的若是須要作攔截處理 能夠直接在interceptCall方法中編寫相應的邏輯

  而後須要在服務端服務類的註解中聲明所使用的攔截器>

1 @GrpcService(value= MultiStreamServiceGrpc.class,interceptors = GrpcInterceptor.class)
2 public class GrpcMultiStreamService extends MultiStreamServiceGrpc.MultiStreamServiceImplBase{
3 //此處略
4 }

攔截器聲明能夠見以上代碼紅色部分,以上代碼的具體邏輯部分與以上GrpcMultiStreamService內容相同,同時順帶說下上面註解中的value變量,這個變量只是聲明當前服務端服務類所使用的grpc的服務類是什麼,固然能夠填寫其餘的grpc的服務類(必定是proto文件生成的類才能夠),而且不能爲空!,同時這裏就不給測試結果囖,讀者打個斷點就知道了。

 

如今是: 2018-09-01 19:39:12 

  各位晚安~  

相關文章
相關標籤/搜索