gRPC中interceptor攔截器的使用教程
一、使用場(chǎng)景
gRPC中的interceptor攔截器分為客戶端攔截器和服務(wù)端攔截器,分別是在客戶端和服務(wù)端的請(qǐng)求被發(fā)送出去之前進(jìn)行處理的邏輯。常見的使用場(chǎng)景有:(1)請(qǐng)求日志記錄及監(jiān)控;(2)添加請(qǐng)求頭數(shù)據(jù)、以便代理轉(zhuǎn)發(fā)使用;(3)請(qǐng)求或者結(jié)果重寫。
二、原理分析
1.interceptor介紹
攔截器是調(diào)用在還沒有到達(dá)目的地之前進(jìn)行處理的邏輯,類似于Spring框架中存在的Interceptor。
gRPC 攔截器主要分為兩種:客戶端攔截器(ClientInterceptor),服務(wù)端攔截器(ServerInterceptor),顧名思義,分別于請(qǐng)求的兩端執(zhí)行相應(yīng)的前攔截處理。
2.使用方法說明
2.1.ClientInterceptor 源碼
@ThreadSafe
public interface ClientInterceptor {
? <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
? ? ? MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next);
}它只有一個(gè)方法:interceptCall,對(duì)于注冊(cè)了相應(yīng)攔截器的客戶端調(diào)用,都要經(jīng)過這個(gè)方法。
參數(shù):
1、method:MethodDescriptor 類型,標(biāo)示請(qǐng)求方法。包括方法全限定名稱、請(qǐng)求服務(wù)名稱、請(qǐng)求、結(jié)果、序列化工具、冪等等。
2、callOptions:此次請(qǐng)求的附帶信息。
3、next:執(zhí)行此次 RPC 請(qǐng)求的抽象鏈接管道(Channel)
返回:
ClientCall,包含請(qǐng)求及結(jié)果信息,并且不為null。
2.2.ServerInterceptor 源碼
@ThreadSafe
public interface ServerInterceptor {
? <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
? ? ? ServerCall<ReqT, RespT> call,
? ? ? Metadata headers,
? ? ? ServerCallHandler<ReqT, RespT> next);
}它只有一個(gè)方法:interceptCall,對(duì)于注冊(cè)了相應(yīng)攔截器的服務(wù)端調(diào)用,都要經(jīng)過這個(gè)方法。
參數(shù):
- call:ServerCall 對(duì)象,包含客戶端請(qǐng)求的 MethodDescriptor
- headers:請(qǐng)求頭信息
- next:處理鏈條上的下一個(gè)處理。
三、代碼實(shí)踐
1.實(shí)現(xiàn)功能
通過代碼實(shí)現(xiàn)以下功能:
- 使用grpc打印日志;
- 獲取header信息,返回header信息;
- 使用grpc獲取ip信息,獲取客戶端傳遞過來的信息;
2.服務(wù)端實(shí)現(xiàn)代碼
(1)實(shí)現(xiàn)自定義ServerGrpcInterceptor
只需要實(shí)現(xiàn)ServerInterceptor接口,只需要重寫interceptCall方法
import io.grpc.*;
import io.grpc.netty.shaded.io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.Map;
/**
* @author yangnk
* @desc
* @date 2023/08/07 23:17
**/
@Slf4j
public class MyServerGrpcInterceptor implements ServerInterceptor {
??? @Override
??? public? ServerCall.Listener interceptCall(ServerCall serverCall, Metadata metadata, ServerCallHandler serverCallHandler) {
??????? //1.打印請(qǐng)求方法
??????? log.info("請(qǐng)求方法:{}", serverCall.getMethodDescriptor());
??????? //2.從請(qǐng)求的屬性中獲取遠(yuǎn)程地址
??????? String remoteAddr = serverCall.getAttributes().get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR).toString();
??????? log.info("遠(yuǎn)程地址為:{}", remoteAddr);
??????? //3.獲取header中的參數(shù)進(jìn)行業(yè)務(wù)處理
??????? Map map = new HashMap();
??????? map.put("00000001", "admin");
??????? //獲取header中參數(shù)
??????? Metadata.Key token = Metadata.Key.of("token", Metadata.ASCII_STRING_MARSHALLER);
??????? Metadata.Key userId = Metadata.Key.of("userId", Metadata.ASCII_STRING_MARSHALLER);
??????? String tokenStr = metadata.get(token);
??????? if (StringUtil.isNullOrEmpty(tokenStr)){
??????????? System.err.println("未收到客戶端token,關(guān)閉此連接");
??????????? serverCall.close(Status.DATA_LOSS,metadata);
??????? }
??????? //獲得token去中查詢
??????? String userInfo = map.get(metadata.get(userId));
??????? if(StringUtil.isNullOrEmpty(userInfo)){
??????????? System.err.println("客戶端token錯(cuò)誤,關(guān)閉此連接");
??????????? serverCall.close(Status.DATA_LOSS,metadata);
??????? }
??????? //服務(wù)端寫回參數(shù)
??????? ServerCall newServerCall = new ForwardingServerCall.SimpleForwardingServerCall(serverCall) {
??????????? @Override
??????????? public void sendHeaders(Metadata headers) {
??????????????? headers.put(userId,userInfo);
??????????????? super.sendHeaders(headers);
??????????? }
??????? };
??????? return serverCallHandler.startCall(newServerCall, metadata);
??? }
}(2)全局配置ServerGrpcInterceptor
通過@GrpcGlobalServerInterceptor注解配置Interceptor
import com.yangnk.grpcserver.dialoutService.DialoutGrpcInterceptor;
import com.yangnk.grpcserver.dialoutService.MyServerGrpcInterceptor;
import io.grpc.ServerInterceptor;
import net.devh.boot.grpc.server.interceptor.GrpcGlobalServerInterceptor;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class GlobalInterceptorConfiguration {
??? @GrpcGlobalServerInterceptor
??? ServerInterceptor myServerInterceptor() {
??????? return new MyServerGrpcInterceptor();
??? }
}3.客戶端實(shí)現(xiàn)代碼
(1)實(shí)現(xiàn)自定義ClientGrpcInterceptor
只需要實(shí)現(xiàn)ClientInterceptor接口,只需要重寫interceptCall方法
import io.grpc.*;
import lombok.extern.slf4j.Slf4j;
/**
* @author yangnk
* @desc
* @date 2023/08/08 00:15
**/
@Slf4j
public class MyClientGrpcInterceptor implements ClientInterceptor {
??? @Override
??? public? ClientCall interceptCall(MethodDescriptor method, CallOptions callOptions, Channel next) {
??????? Metadata.Key token = Metadata.Key.of("token", Metadata.ASCII_STRING_MARSHALLER);
??????? Metadata.Key userId = Metadata.Key.of("userId", Metadata.ASCII_STRING_MARSHALLER);
??????? //1.打印日志
??????? log.info("請(qǐng)求名稱:{}", method.getFullMethodName());
??????? //2.請(qǐng)求參數(shù)放到header中
??????? return new ForwardingClientCall.SimpleForwardingClientCall(next.newCall(method, callOptions)) {
??????????? @Override
??????????? public void start(Listener responseListener, Metadata headers) {
??????????????? //此處為你登錄后獲得的token的值
??????????????? headers.put(userId, "00000001");
??????????????? headers.put(token, "A2D05E5ED2414B1F8C6AEB19F40EF77C");
??????????????? super.start(new ForwardingClientCallListener.SimpleForwardingClientCallListener(responseListener) {
??????????????????? @Override
??????????????????? public void onHeaders(Metadata headers) {
??????????????????????? log.info("請(qǐng)求返回信息為:" + headers);
??????????????????????? super.onHeaders(headers);
??????????????????? }
??????????????? }, headers);
??????????? }
??????? };
??? }
}(2)全局配置ClientGrpcInterceptor
通過@GrpcGlobalClientInterceptor注解配置Interceptor
import io.grpc.ClientInterceptor;
import net.devh.boot.grpc.client.interceptor.GrpcGlobalClientInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
@Order(Ordered.LOWEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
public class GlobalClientInterceptorConfiguration {
??? @GrpcGlobalClientInterceptor
??? ClientInterceptor myClientInterceptor() {
??????? return new MyClientGrpcInterceptor();
??? }
}4.驗(yàn)證結(jié)果
服務(wù)端請(qǐng)求結(jié)果:

客戶端請(qǐng)求結(jié)果:

代碼地址:https://github.com/yangnk/SpringBoot_Learning/tree/master/GRPCDemo
以上就是gRPC中interceptor攔截器的使用教程的詳細(xì)內(nèi)容,更多關(guān)于interceptor攔截器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
mysql+spring+mybatis實(shí)現(xiàn)數(shù)據(jù)庫讀寫分離的代碼配置
今天小編就為大家分享一篇關(guān)于mysql+spring+mybatis實(shí)現(xiàn)數(shù)據(jù)庫讀寫分離的代碼配置,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-03-03
java通過方法來交換實(shí)參的值實(shí)現(xiàn)方式
文章介紹了Java方法參數(shù)傳遞方式以及如何通過數(shù)組、自定義包裝類或返回結(jié)果來實(shí)現(xiàn)變量交換,同時(shí),討論了內(nèi)部類中static關(guān)鍵字的作用和使用場(chǎng)景2025-12-12
Java中Druid連接池連接超時(shí)獲取不到連接的解決
這篇文章主要介紹了Java中Druid連接池連接超時(shí)獲取不到連接的解決,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
Java代理模式之靜態(tài)代理與動(dòng)態(tài)代理詳解
Java代理分為靜態(tài)代理和動(dòng)態(tài)代理,靜態(tài)代理需手動(dòng)編寫代理類,適合少量對(duì)象,動(dòng)態(tài)代理運(yùn)行時(shí)自動(dòng)生成代理類,適用于大量對(duì)象,如Spring AOP,這篇文章主要介紹了Java代理模式之靜態(tài)代理與動(dòng)態(tài)代理的相關(guān)資料,需要的朋友可以參考下2026-01-01
SpringBoot 中html的頁面間跳轉(zhuǎn)問題小結(jié)
這篇文章主要介紹了SpringBoot 中html的頁面間跳轉(zhuǎn)問題小結(jié),本文給大家分享兩種方法,結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-10-10
PowerJob的AliOssService工作流程源碼解讀
這篇文章主要介紹了PowerJob的AliOssServiceg工作流程源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01

