<meter id="blnnv"></meter>

                  社區精選|聊一聊 gRPC 中的攔截器

                  業界 作者:SegmentFault 2023-02-24 22:31:27

                  今天小編為大家帶來的是社區作者 江南一點雨?的文章,讓我們一起繼續 gRPC 系列




                  前面松哥跟大家聊了 gRPC 的簡單案例,也說了四種不同的通信模式,感興趣的小伙伴可以戳這里:

                  • 一個簡單的案例入門 gRPC
                  • https://mp.weixin.qq.com/s/OyfU0tLm4f9t3nZxce-Ksw
                  • 聊一聊 gRPC 的四種通信模式
                  • https://mp.weixin.qq.com/s/c-_D2RpLksIlYJDfaWOSkA

                  今天我們來繼續聊一聊 gRPC 中的攔截器。

                  有請求的發送、處理,當然就會有攔截器的需求,例如在服務端通過攔截器統一進行請求認證等操作,這些就需要攔截器來完成,今天松哥先和小伙伴們來聊一聊 gRPC 中攔截器的基本用法,后面我再整一篇文章和小伙伴們做一個基于攔截器實現的 JWT 認證的 gRPC。

                  gRPC 中的攔截器整體上來說可以分為兩大類:

                  1. 服務端攔截器
                  2. 客戶端攔截器

                  我們分別來看。

                  1. 服務端攔截器


                  服務端攔截器的作用有點像我們 Java 中的 Filter,服務端攔截器又可以繼續細分為一元攔截器和流攔截器。

                  一元攔截器對應我們上篇文章中所講的一元 RPC,也就是一次請求,一次響應這種情況。

                  流攔截器則對應我們上篇文章中所講的服務端流 RPC、客戶端流 RPC 以及雙向流 RPC。

                  不過,在 Java 代碼中,無論是一元攔截器還是流攔截器,代碼其實都是一樣的。不過如果你是用 Go 實現的 gRPC,那么這塊是不一樣的。

                  所以接下來的內容我就不去區分一元攔截器和流攔截器了,我們直接來看一個服務端攔截器的例子。

                  這里我就不從頭開始寫了,我們直接在上篇文章的基礎之上繼續添加攔截器即可。

                  服務端攔截器工作位置大致如下:


                  從這張圖中小伙伴們可以看到,我們可以在服務端處理請求之前將請求攔截下來,統一進行權限校驗等操作,也可以在服務端將請求處理完畢之后,準備響應的時候將響應攔截下來,可以對響應進行二次處理。

                  首先我們來看請求攔截器,實際上是一個監聽器:

                  public?class?BookServiceCallListener<R>?extends?ForwardingServerCallListener<R>?{
                  ????private?final?ServerCall.Listener<R>?delegate;

                  ????public?BookServiceCallListener(ServerCall.Listener<R>?delegate)?{
                  ????????this.delegate?=?delegate;
                  ????}

                  ????@Override
                  ????protected?ServerCall.Listener<R>?delegate()?{
                  ????????return?delegate;
                  ????}

                  ????@Override
                  ????public?void?onMessage(R?message)?{
                  ????????System.out.println("這是客戶端發來的消息,可以在這里進行預處理:"+message);
                  ????????super.onMessage(message);
                  ????}
                  }

                  這里我們自定義一個類,繼承自 ForwardingServerCallListener 類,在這里重寫 onMessage 方法,當有請求到達的時候,就會經過這里的 onMessage 方法。如果我們需要對傳入的參數進行驗證等操作,就可以在這里完成。

                  再來看看響應攔截器:

                  public?class?BookServiceCall<ReqT,RespT>?extends?ForwardingServerCall.SimpleForwardingServerCall<ReqT,RespT>?{
                  ????protected?BookServiceCall(ServerCall<ReqT,?RespT>?delegate)?{
                  ????????super(delegate);
                  ????}

                  ????@Override
                  ????protected?ServerCall<ReqT,?RespT>?delegate()?{
                  ????????return?super.delegate();
                  ????}

                  ????@Override
                  ????public?MethodDescriptor<ReqT,?RespT>?getMethodDescriptor()?{
                  ????????return?super.getMethodDescriptor();
                  ????}

                  ????@Override
                  ????public?void?sendMessage(RespT?message)?{
                  ????????System.out.println("這是服務端返回給客戶端的消息:"+message);
                  ????????super.sendMessage(message);
                  ????}
                  }

                  小伙伴們可能發現了,我這里用到了很多泛型,請求類型和響應類型都不建議指定具體類型,因為攔截器可能會攔截多種類型的請求,請求參數和響應的數據類型都不一定一樣。

                  這里是重寫 sendMessage 方法,在這個方法中我們可以對服務端準備返回給客戶端的消息進行預處理。

                  所以這個位置就相當于響應攔截器。

                  最后,我們需要在啟動服務的時候,將這兩個攔截器配置進去,代碼如下:

                  public?void?start()?throws?IOException?{
                  ????int?port?=?50051;
                  ????server?=?ServerBuilder.forPort(port)
                  ????????????.addService(ServerInterceptors.intercept(new?BookServiceImpl(),?new?ServerInterceptor()?{
                  ????????????????@Override
                  ????????????????public?<ReqT,?RespT>?ServerCall.Listener<ReqT>?interceptCall(ServerCall<ReqT,?RespT>?call,?Metadata?headers,?ServerCallHandler<ReqT,?RespT>?next)?{
                  ????????????????????String?fullMethodName?=?call.getMethodDescriptor().getFullMethodName();
                  ????????????????????System.out.println(fullMethodName?+?":pre");
                  ????????????????????Set<String>?keys?=?headers.keys();
                  ????????????????????for?(String?key?:?keys)?{
                  ????????????????????????System.out.println(key?+?">>>"?+?headers.get(Metadata.Key.of(key,?ASCII_STRING_MARSHALLER)));
                  ????????????????????}
                  ????????????????????return?new?BookServiceCallListener<>(next.startCall(new?BookServiceCall(call),?headers));
                  ????????????????}
                  ????????????}))
                  ????????????.build()
                  ????????????.start();
                  ????Runtime.getRuntime().addShutdownHook(new?Thread(()?->?{
                  ????????BookServiceServer.this.stop();
                  ????}));
                  }

                  這是我之前服務啟動的方法,以前我們調用 addService 方法的時候,直接添加對應的服務就可以了,現在,我們除了添加之前的 BookServiceImpl 服務之外,還額外給了一個攔截器。

                  每當請求到達的時候,就會經過攔截器的 interceptCall 方法,這個方法有三個參數:

                  • 第一個參數 call 是消費傳入的 RPC 消息的一個回調。
                  • 第二個參數 headers 則是請求的消息頭,如果我們通過 JWT 進行請求校驗,那么就從這個 headers 中提取出請求的 JWT 令牌然后進行校驗。
                  • 第三個參數 next 就類似于我們在 Java 過濾器 filter 中的 filterChain 一樣,讓這個請求繼續向下走。

                  在這個方法中,我們請求頭的信息都打印出來給小伙伴們參考了。然后在返回值中,將我們剛剛寫的請求攔截器和響應攔截器構建并返回。

                  好啦,這樣我們的服務端攔截器就搞好啦~無論是一元的 RPC 消息還是流式的 RPC 消息,都會經過這個攔截器,響應也是一樣。

                  2. 客戶端攔截器



                  客戶端攔截器就比較簡單了,客戶端攔截器可以將我們的請求攔截下來,例如我們如果想為所有請求添加統一的令牌 Token,那么就可以在這里來做,方式如下:

                  ManagedChannel?channel?=?ManagedChannelBuilder.forAddress("localhost",?50051)
                  ????????.usePlaintext()
                  ????????.intercept(new?ClientInterceptor()?{
                  ????????????@Override
                  ????????????public?<ReqT,?RespT>?ClientCall<ReqT,?RespT>?interceptCall(MethodDescriptor<ReqT,?RespT>?method,?CallOptions?callOptions,?Channel?next)?{
                  ????????????????System.out.println("!!!!!!!!!!!!!!!!");
                  ????????????????callOptions?=?callOptions.withAuthority("javaboy");
                  ????????????????return?next.newCall(method,callOptions);
                  ????????????}
                  ????????})
                  ????????.build();
                  BookServiceGrpc.BookServiceStub?stub?=?BookServiceGrpc.newStub(channel);



                  當我們的請求執行的時候,這個客戶端攔截器就會被觸發。

                  3. 小結



                  好啦,今天就和小伙伴們簡單介紹一下服務端攔截器和客戶端攔截器。下篇文章,松哥會通過一個 JWT 認證來和小伙伴們演示這個攔截器的具體用法。



                  點擊左下角閱讀原文,到?SegmentFault 思否社區?和文章作者展開更多互動和交流,“公眾號后臺回復“?入群?”即可加入我們的技術交流群,收獲更多的技術文章~

                  -?END -


                  關注公眾號:拾黑(shiheibook)了解更多

                  贊助鏈接:

                  關注數據與安全,洞悉企業級服務市場:https://www.ijiandao.com/
                  四季很好,只要有你,文娛排行榜:https://www.yaopaiming.com/
                  讓資訊觸達的更精準有趣:https://www.0xu.cn/

                  公眾號 關注網絡尖刀微信公眾號
                  隨時掌握互聯網精彩
                  贊助鏈接
                  一级毛片丰满奶头出奶水,国产成人精品午夜福利2010,亚洲欧美激情精品一区二区,色欲av无码蜜臀AV免费播放,夜夜爽夜夜叫夜夜高潮漏水,av无码aV高潮αV喷吹免费,无码vr熟妇人妻AV在线影片