使用支援 @GRpcService 的 bean 自動配置並運行嵌入式 gRPC 伺服器,作為 spring-boot 應用程式的一部分(短視頻)
建議Gradle
用戶應用該插件:
plugins {
id " io.github.lognet.grpc-spring-boot " version ' 5.1.5 '
}
io.github.lognet.grpc-spring-boot gradle 插件大大簡化了專案設定。
repositories {
mavenCentral()
// maven { url "https://oss.sonatype.org/content/repositories/snapshots" } // for snapshot builds
}
dependencies {
implementation ' io.github.lognet:grpc-spring-boot-starter:5.1.5 '
}
預設情況下,如果您被迫使用純grpc-netty
依賴項,starter 會拉取io.grpc:grpc-netty-shaded
作為傳遞依賴項:
implementation ( ' io.github.lognet:grpc-spring-boot-starter:5.1.5 ' ) {
exclude group : ' io.grpc ' , module : ' grpc-netty-shaded '
}
implementation ' io.grpc:grpc-netty:1.58.0 ' // (1)
確保拉取與發布版本相符的版本。
grpc.netty-server.on-collision-prefer-shaded-netty
屬性也支援這兩個函式庫在類別路徑上的存在。
如果您使用 Spring Boot 依賴管理插件,它可能會提取與編譯啟動的版本不同的版本,從而導致二進位不相容問題。
在這種情況下,您需要強制且明確地設定要使用的grpc
版本(請參閱此處的版本矩陣):
configurations . all {
resolutionStrategy . eachDependency { details ->
if ( " io.grpc " . equalsIgnoreCase(details . requested . group)) {
details . useVersion " 1.58.0 "
}
}
}
可以在此處找到帶有相容性矩陣的發行說明 |
請依照本指南從.proto
檔案產生存根和伺服器介面。
如果您使用 Maven - 請使用此連結。
使用@org.lognet.springboot.grpc.GRpcService
註釋您的伺服器介面實現
可選擇在application.yml/properties
中設定伺服器連接埠。預設連接埠為6565
。
grpc :
port : 6565
可以透過將連接埠設為0 來定義隨機連接埠。然後可以透過在 int 欄位上使用@LocalRunningGrpcPort 註釋來檢索正在使用的實際端口,該註釋將注入正在運行的端口(明確配置或隨機選擇) |
可選擇啟用伺服器反射(請參閱https://github.com/grpc/grpc-java/blob/master/documentation/server-reflection-tutorial.md)
grpc :
enableReflection : true
可以選擇設定啟動階段順序(預設為Integer.MAX_VALUE
)。
grpc :
start-up-phase : XXX
(可選)設定伺服器正常關閉期間等待先前存在的呼叫完成的秒數。在此期間新的呼叫將被拒絕。負值相當於無限寬限期。預設值為0
(表示不等待)。
grpc :
shutdownGrace : 30
Netty 特定的伺服器屬性可以在grpc.netty-server
前綴下指定。
透過設定grpc.netty-server.xxxx
值之一,您可以隱含地將傳輸設定為基於 Netty 的。
grpc :
netty-server :
keep-alive-time : 30s (1)
max-inbound-message-size : 10MB (2)
primary-listen-address : 10.10.15.23:0 (3)
additional-listen-addresses :
- 192.168.0.100:6767 (4)
on-collision-prefer-shaded-netty : false (5)
Duration
類型屬性可以使用此處描述的字串值格式進行配置。
DataSize
類型屬性可以使用此處所述的字串值進行配置
使用自訂連接埠暴露在外部網路 IP 上。
SocketAddress
類型屬性字串值格式:
host:port
(如果port
值小於1,則使用隨機值)
host:
使用預設 grpc 連接埠6565
)
也透過預定義連接埠6767
暴露在內部網路 IP 上。
如果依賴項中同時有shaded
庫和pure
netty 庫,請選擇應建立的NettyServerBuilder
類型。這是將傳遞給GRpcServerBuilderConfigurer
的類型(請參閱自訂 gRPC 伺服器配置),預設為true
(即io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder
; io.grpc.netty.NettyServerBuilder
如果false
)
啟動器還支援in-process server
,該伺服器應用於測試目的:
grpc :
enabled : false (1)
inProcessServerName : myTestServer (2)
禁用預設伺服器 ( NettyServer
)。
啟用in-process
伺服器。
如果同時啟用NettyServer 和in-process 伺服器,它們將共用HealthStatusManager 和GRpcServerBuilderConfigurer 的相同實例(請參閱自訂 gRPC 伺服器設定)。 |
在grpc-spring-boot-starter-demo
專案中,您可以找到具有整合測試的功能齊全的範例。
.proto
檔案中的服務定義如下所示:
service Greeter {
rpc SayHello ( HelloRequest ) returns ( HelloReply ) {}
}
請注意產生的io.grpc.examples.GreeterGrpc.GreeterImplBase
類,該類別擴展了io.grpc.BindableService
。
您需要做的就是使用@org.lognet.springboot.grpc.GRpcService
註釋您的服務實現
@ GRpcService
public static class GreeterService extends GreeterGrpc . GreeterImplBase {
@ Override
public void sayHello ( GreeterOuterClass . HelloRequest request , StreamObserver < GreeterOuterClass . HelloReply > responseObserver ) {
final GreeterOuterClass . HelloReply . Builder replyBuilder = GreeterOuterClass . HelloReply . newBuilder (). setMessage ( "Hello " + request . getName ());
responseObserver . onNext ( replyBuilder . build ());
responseObserver . onCompleted ();
}
}
Starter支援兩種攔截器的註冊: Global和Per Service 。
在這兩種情況下,攔截器都必須實作io.grpc.ServerInterceptor
介面。
每項服務
@ GRpcService ( interceptors = { LogInterceptor . class })
public class GreeterService extends GreeterGrpc . GreeterImplBase {
// ommited
}
如果存在LogInterceptor
類型的 bean,則LogInterceptor
將透過 spring 工廠實例化,否則透過無參數建構子實例化。
全球的
@ GRpcGlobalInterceptor
public class MyInterceptor implements ServerInterceptor {
// ommited
}
也支援 java 配置工廠方法上的註解:
@ Configuration
public class MyConfig {
@ Bean
@ GRpcGlobalInterceptor
public ServerInterceptor globalInterceptor (){
return new ServerInterceptor (){
@ Override
public < ReqT , RespT > ServerCall . Listener < ReqT > interceptCall ( ServerCall < ReqT , RespT > call , Metadata headers , ServerCallHandler < ReqT , RespT > next ) {
// your logic here
return next . startCall ( call , headers );
}
};
}
}
特定服務還有機會停用全域攔截器:
@ GRpcService ( applyGlobalInterceptors = false )
public class GreeterService extends GreeterGrpc . GreeterImplBase {
// ommited
}
全域攔截器可以使用 Spring 的@Ordered
或@Priority
註解進行排序。遵循 Spring 的排序語義,順序較低的值具有較高的優先級,並且將在攔截器鏈中首先執行。
@ GRpcGlobalInterceptor
@ Order ( 10 )
public class A implements ServerInterceptor {
// will be called before B
}
@ GRpcGlobalInterceptor
@ Order ( 20 )
public class B implements ServerInterceptor {
// will be called after A
}
啟動器使用內建攔截器來實現錯誤處理、Spring Security
、 Validation
和Metrics
整合。它們的順序也可以透過以下屬性控制:
grpc.recovery.interceptor-order
(錯誤處理攔截器順序,預設為Ordered.HIGHEST_PRECEDENCE
)
grpc.security.auth.interceptor-order
(預設為Ordered.HIGHEST_PRECEDENCE+1
)
grpc.validation.interceptor-order
(預設為Ordered.HIGHEST_PRECEDENCE+10
)
grpc.metrics.interceptor-order
(預設為Ordered.HIGHEST_PRECEDENCE+20
)
這使您能夠設定內建攔截器和自訂攔截器的所需順序。
繼續閱讀!還有更多
grpc 攔截器的工作方式是攔截呼叫並返回伺服器呼叫偵聽器,伺服器呼叫偵聽器反過來也可以攔截請求訊息,然後將其轉發到實際的服務呼叫處理程序:
透過將grpc.security.auth.fail-fast
屬性設定為false
在驗證/授權失敗的情況下,所有下游攔截器以及所有上游攔截器 (On_Message) 仍將被執行
假設interceptor_2
是securityInterceptor
:
對於grpc.security.auth.fail-fast=true
(預設)失敗的身份驗證/授權:
對於grpc.security.auth.fail-fast=false
失敗的身份驗證/授權:
這個啟動是由spring-cloud-sleuth
計畫原生支援的。
請繼續探索 grpc 整合。
透過包含org.springframework.boot:spring-boot-starter-actuator
依賴項,啟動器將收集 gRPC 伺服器指標,細分為
method
- gRPC 服務方法 FQN(完全限定名稱)
result
- 回應狀態代碼
address
- 伺服器本機位址(如果您使用grpc.netty-server.additional-listen-addresses
屬性公開了其他偵聽位址)
配置您選擇的匯出器後,您應該會看到名為grpc.server.calls
的timer
。
透過在應用程式上下文中定義GRpcMetricsTagsContributor
bean,您可以將自訂標籤新增至grpc.server.calls
計時器。
您也可以使用RequestAwareGRpcMetricsTagsContributor
bean 來標記一元和流呼叫。
演示在這裡
保持較低的離散度,以免誇大度量的基數。 |
如果metric
攔截器的優先權高於security
攔截器且grpc.security.auth.fail-fast
設定為false
,則RequestAwareGRpcMetricsTagsContributor
仍可以在驗證失敗時執行。
本測試涵蓋了這種情況。
請務必閱讀攔截器訂購章節。 |
確保包含以下相依性:
implementation "org.springframework.boot:spring-boot-starter-actuator"
implementation "io.micrometer:micrometer-registry-prometheus"
implementation 'org.springframework.boot:spring-boot-starter-web'
配置 :
management :
metrics :
export :
prometheus :
enabled : true
endpoints :
web :
exposure :
include : " * "
標準/actuator/metrics
和/actuator/prometheus
端點將呈現grpc.server.calls
指標(請參閱此處的演示)。
GRPC報廢提案 |
可以自動配置啟動器以驗證請求/回應 gRPC 服務訊息。請繼續實施訊息驗證以取得配置詳細資訊。
啟動器內部定義了java.util.function.Consumer
類型的 bean,當spring-cloud-stream
位於類路徑上時,該 bean 被考慮用於函數註冊,這是不可取的(如果您恰好有, spring-cloud-stream
會自動註冊通道)應用程式上下文中的一個 Consumer/Supplier/Function bean,因此如果您將此啟動器與spring-cloud-stream
一起使用,那麼您已經擁有了一個。
據此,建議在生產就緒應用程式中使用spring.cloud.function.definition
屬性,而不是依賴自動發現。
請參考GRPC Kafka Stream demo,最核心的部分就是這一行。
Starter 為利用與 Spring Security 框架的整合對使用者進行身份驗證和授權提供了內建支援。
有關支援的身份驗證提供者和配置選項的詳細信息,請參閱 Spring Security 整合部分。
可以使用根憑證及其私鑰路徑來設定傳輸安全性:
grpc :
security :
cert-chain : classpath:cert/server-cert.pem
private-key : file:../grpc-spring-boot-starter-demo/src/test/resources/cert/server-key.pem
這兩個屬性的值採用 ResourceEditor 支援的形式。
客戶端應進行相應配置:
(( NettyChannelBuilder ) channelBuilder )
. useTransportSecurity ()
. sslContext ( GrpcSslContexts . forClient (). trustManager ( certChain ). build ());
預設情況下,此啟動程序將拉取io.netty:netty-tcnative-boringssl-static
相依性以支援 SSL。
如果您需要其他 SSL/TLS 支持,請排除此依賴項並遵循安全指南。
如果安全性設定需要更詳細的調整,請使用自訂 gRPC 伺服器設定中所述的自訂設定器 |
若要攔截用於建置io.grpc.Server
io.grpc.ServerBuilder
實例,您可以將從org.lognet.springboot.grpc.GRpcServerBuilderConfigurer
繼承的 bean 新增到您的上下文並覆寫configure
方法。
也支援多個配置器。
在呼叫configure
方法時,所有發現的服務(包括它們的攔截器)都已加入傳遞的建構器。
在configure
方法的實作中,您可以新增自訂配置:
@ Component
public class MyGRpcServerBuilderConfigurer extends GRpcServerBuilderConfigurer {
@ Override
public void configure ( ServerBuilder <?> serverBuilder ){
serverBuilder
. executor ( YOUR EXECUTOR INSTANCE )
. useTransportSecurity ( YOUR TRANSPORT SECURITY SETTINGS );
(( NettyServerBuilder ) serverBuilder ) // cast to NettyServerBuilder (which is the default server) for further customization
. sslContext ( GrpcSslContexts // security fine tuning
. forServer (...)
. trustManager (...)
. build ())
. maxConnectionAge (...)
. maxConnectionAgeGrace (...);
}
};
}
@ Component
public class MyCustomCompressionGRpcServerBuilderConfigurer extends GRpcServerBuilderConfigurer {
@ Override
public void configure ( ServerBuilder <?> serverBuilder ){
serverBuilder
. compressorRegistry ( YOUR COMPRESSION REGISTRY )
. decompressorRegistry ( YOUR DECOMPRESSION REGISTRY ) ;
}
};
}
如果同時啟用NettyServer 和in-process 伺服器,則會在配置器的相同實例上呼叫configure 方法。如果需要區分傳入的 serverBuilder ,可以檢查類型。這是當前的限制。 |
GRpcServerInitializedEvent
在伺服器啟動時發布,您可以使用常規 spring API 來使用它。
從版本5.1.0
開始, spring-boot-starter-gradle-plugin 整合了 SalesForce 的reactive-grpc protoc 插件:
import org.lognet.springboot.grpc.gradle.ReactiveFeature
plugins {
id " io.github.lognet.grpc-spring-boot "
}
grpcSpringBoot {
reactiveFeature . set( ReactiveFeature . REACTOR ) // or ReactiveFeature.RX
}
以下是測試和響應式 grpc 範例服務。
啟動器註冊GRpcExceptionHandlerInterceptor
,它負責將服務拋出的異常傳播到錯誤處理程序。
可以透過使用@GRpcServiceAdvice
註解的 bean 以及使用@GRpcExceptionHandler
註解註解的方法來註冊錯誤處理方法。
這些被視為global
錯誤處理程序,並且呼叫類型層次結構中最接近所引發異常的異常類型參數的方法。
錯誤處理程序的簽章必須遵循以下模式:
返回類型 | 參數1 | 參數2 |
---|---|---|
io.grpc.狀態 | 任何 | GRpc異常範圍 |
@ GRpcServiceAdvice
class MyHandler1 {
@ GRpcExceptionHandler
public Status handle ( MyCustomExcpetion exc , GRpcExceptionScope scope ){
}
@ GRpcExceptionHandler
public Status handle ( IllegalArgumentException exc , GRpcExceptionScope scope ){
}
}
@ GRpcServiceAdvice
class MyHandler2 {
@ GRpcExceptionHandler
public Status anotherHandler ( NullPointerException npe , GRpcExceptionScope scope ){
}
}
您可以擁有任意數量的advice
bean 和處理程序方法,只要它們不會相互幹擾並且不會創建已處理異常類型的歧義。
grpc
服務 bean 也被發現用於錯誤處理程序,其優先權高於@GRpcServiceAdvice
bean 中發現的全域錯誤處理方法。服務等級的錯誤處理方法被認為private
,並且僅當該服務拋出異常時才被呼叫:
class SomeException extends Exception {
}
class SomeRuntimeException extends RuntimeException {
}
@ GRpcService
public class HelloService extends GreeterGrpc . GreeterImplBase {
@ Override
public void sayHello ( GreeterOuterClass . HelloRequest request , StreamObserver < GreeterOuterClass . HelloReply > responseObserver ) {
...
throw new GRpcRuntimeExceptionWrapper ( new SomeException ()) ; // (1)
//or
throw new GRpcRuntimeExceptionWrapper ( new SomeException (), "myHint" ); // (2)
//or
throw new SomeRuntimeException (); //(3)
}
@ GRpcExceptionHandler
public Status privateHandler ( SomeException npe , GRpcExceptionScope scope ){
// INVOKED when thrown from HelloService service
String myHint = scope . getHintAs ( String . class ); // (4)
scope . getResponseHeaders (). put ( Metadata . Key . of ( "custom" , Metadata . ASCII_STRING_MARSHALLER ), "Value" ); // (5)
}
@ GRpcExceptionHandler
public Status privateHandler ( SomeRuntimeException npe , GRpcExceptionScope scope ){
// INVOKED when thrown from HelloService service
}
}
@ GRpcServiceAdvice
class MyHandler {
@ GRpcExceptionHandler
public Status anotherHandler ( SomeException npe , GRpcExceptionScope scope ){
// NOT INVOKED when thrown from HelloService service
}
@ GRpcExceptionHandler
public Status anotherHandler ( SomeRuntimeException npe , GRpcExceptionScope scope ){
// NOT INVOKED when thrown from HelloService service
}
}
由於grpc
服務 API 的性質不允許拋出檢查異常,因此提供了特殊的運行時異常類型來包裝檢查異常。然後在查找處理程序方法時將其解開。
當拋出GRpcRuntimeExceptionWrapper
異常時,您也可以傳遞hint
對象,然後可以從handler
方法中的scope
物件存取該提示物件。
運行時異常可以原樣拋出,不需要包裝。
取得提示對象。
將自訂標頭髮送給客戶端。
身份驗證失敗透過AuthenticationException
傳播,授權失敗透過AccessDeniedException
傳播。
驗證失敗透過ConstraintViolationException
傳播:對於失敗的請求 - 以Status.INVALID_ARGUMENT
作為提示,對於失敗的回應 - 以Status.FAILED_PRECONDITION
作為提示。
演示在這裡
由於透過 XML 部署描述符提供了 Bean Validation 配置支持,因此可以透過 XML 為生成的類別提供約束,而不是使用自訂protoc
編譯器來偵測產生的訊息。
將org.springframework.boot:spring-boot-starter-validation
依賴項新增到您的專案中。
建立META-INF/validation.xml
和約束聲明檔。 (IntelliJ IDEA 對授權 bean 驗證約束 xml 檔案有很好的自動完成支援)
另請參閱Hibernate
驗證器文件中的範例
您可以在這裡找到演示配置和相應的測試
請注意, request
和response
訊息都正在驗證。
如果您的 gRPC 方法使用相同的請求和回應訊息類型,您可以使用org.lognet.springboot.grpc.validation.group.RequestMessage
和org.lognet.springboot.grpc.validation.group.ResponseMessage
驗證群組來套用不同的驗證邏輯:
...
< getter name = " someField " >
<!-- should be empty for request message -->
< constraint annotation = " javax.validation.constraints.Size " >
< groups >
< value >org.lognet.springboot.grpc.validation.group.RequestMessage</ value > (1)
</ groups >
< element name = " min " >0</ element >
< element name = " max " >0</ element >
</ constraint >
<!-- should NOT be empty for response message -->
< constraint annotation = " javax.validation.constraints.NotEmpty " >
< groups >
< value >org.lognet.springboot.grpc.validation.group.ResponseMessage</ value > (2)
</ groups >
</ constraint >
</ getter >
...
僅對request
訊息套用此約束
僅對response
訊息套用此約束
另請注意自訂跨字段約束及其用法:
< bean class = " io.grpc.examples.GreeterOuterClass$Person " >
< class >
< constraint annotation = " org.lognet.springboot.grpc.demo.PersonConstraint " />
</ class >
<!-- ... -->
</ bean >
如攔截器排序章節所述,您可以為validation
攔截器提供比security
攔截器更高的優先權,並將grpc.security.auth.fail-fast
屬性設定為false
。
在這種情況下,如果呼叫未經身份驗證且無效,則客戶端將獲得Status.INVALID_ARGUMENT
而不是Status.PERMISSION_DENIED/Status.UNAUTHENTICATED
回應狀態。演示在這裡
雖然仍然可以使用@Transactional
註解你的 rpc 方法(如果預設情況下未啟用,則使用spring.aop.proxy-target-class=true
),但有可能會出現不可預測的行為。考慮下面的 grpc 方法實作:
@ GRpcService
class MyGrpcService extends ...{
@ Autowired
private MyJpaRepository repo ;
@ Transactional //(1)
public void rpcCall ( Req request , StreamOvserver < Res > observer ) {
Res response = // Database operations via repo
observer . onNext ( response ); //(2)
observer . onCompleted ();
} //(3)
}
這個方法被註解為@Transactional
,Spring 會在方法返回後的某個時間提交事務
響應返回給呼叫者
方法返回,事務最終提交。
理論上,正如您所看到的 - 實際上,當客戶端(如果網路延遲很小時,並且您的 grpc 伺服器鼓勵在 <2> 之後立即進行上下文切換)可以嘗試透過另一個 grpc 呼叫存取資料庫之前,時間跨度小交易已提交。
克服這種情況的解決方案是將事務邏輯外部化到單獨的服務類別:
@ Service
class MyService {
@ Autowired
private MyJpaRepository repo ;
@ Transactional //(1)
public Res doTransactionalWork (){
// Database operations via repo
return result ;
} //(2)
}
@ GRpcService
class MyGrpcService extends ...{
@ Autowired
private MyService myService ;
public void rpcCall ( Req request , StreamOvserver < Res > observer ) {
Res response = myService . doTransactionalWork ();
observer . onNext ( response ); //(3)
observer . onCompleted ();
}
}
服務方式是事務性的
交易最終被提交。
事務提交後回覆。
透過遵循這種方法,您還可以解耦傳輸層和業務邏輯,現在可以單獨測試它們。
方案 | 依賴關係 |
---|---|
基本的 |
|
承載者 |
|
風俗 |
|
GRPC 安全性配置遵循與 Spring WEB 安全性配置相同的原則和 API,如果類別路徑中有org.springframework.security:spring-security-config
依賴項,則預設啟用它。
您可以在服務/方法上使用@Secured
註解來保護您的端點,或透過使用 API 和覆寫預設值(這會處理@Secured
註解):
@ Configuration
class MySecurityCfg extends GrpcSecurityConfigurerAdapter {
@ Override
public void configure ( GrpcSecurity builder ) throws Exception {
MethodsDescriptor <?,?> adminMethods = MyServiceGrpc . getSomeMethod ();
builder
. authorizeRequests ()
. methods ( adminMethods ). hasAnyRole ( "admin" )
. anyMethodExcluding ( adminMethods ). hasAnyRole ( "user" )
. withSecuredAnnotation ();( 1 )
}
}
或將API
與@Secured
註解結合。
此預設配置保護使用org.springframework.security.access.annotation.@Secured
註解註解的 GRPC 方法/服務。
將註解的值保留為空( @Secured({})
)表示:僅authenticate
,不會執行任何授權。
如果您的上下文中存在JwtDecoder
bean,它還將註冊JwtAuthenticationProvider
來處理身份驗證聲明的驗證。
BasicAuthSchemeSelector
和BearerTokenAuthSchemeSelector
也會自動註冊,以支援使用使用者名稱/密碼和不記名令牌進行身份驗證。
將grpc.security.auth.enabled
設定為false
,可以關閉 GRPC 安全性。
GRPC安全配置的客製化是透過擴展GrpcSecurityConfigurerAdapter
來完成的(各種設定範例和測試場景都在這裡。)
@ Configuration
public class GrpcSecurityConfiguration extends GrpcSecurityConfigurerAdapter {
@ Autowired
private JwtDecoder jwtDecoder ;
@ Override
public void configure ( GrpcSecurity builder ) throws Exception {
builder . authorizeRequests ()( 1 )
. methods ( GreeterGrpc . getSayHelloMethod ()). hasAnyAuthority ( "SCOPE_profile" )( 2 )
. and ()
. authenticationProvider ( JwtAuthProviderFactory . withAuthorities ( jwtDecoder ));( 3 )
}
}
取得授權配置對象
具有SCOPE_profile
權限的經過驗證的使用者允許sayHello
方法的MethodDefinition
。
使用JwtAuthenticationProvider
針對使用spring.security.oauth2.resourceserver.jwt.issuer-uri
屬性配置的資源伺服器驗證使用者聲明( BEARER
令牌)。
可以透過實作AuthenticationSchemeSelector
介面來插入您自己的自訂身份驗證提供者。
@ Configuration
public class GrpcSecurityConfiguration extends GrpcSecurityConfigurerAdapter {
@ Override
public void configure ( GrpcSecurity builder ) throws Exception {
builder . authorizeRequests ()
. anyMethod (). authenticated () //(1)
. and ()
. authenticationSchemeSelector ( new AuthenticationSchemeSelector () { //(2)
@ Override
public Optional < Authentication > getAuthScheme ( CharSequence authorization ) {
return new MyAuthenticationObject (); // (3)
}
})
. authenticationProvider ( new AuthenticationProvider () { // (4)
@ Override
public Authentication authenticate ( Authentication authentication ) throws AuthenticationException {
MyAuthenticationObject myAuth = ( MyAuthenticationObject ) authentication ;
//validate myAuth
return MyValidatedAuthenticationObject ( withAuthorities ); //(5)
}
@ Override
public boolean supports ( Class <?> authentication ) {
return MyAuthenticationObject . class . isInstance ( authentication );
}
});
}
}
保護所有服務方法。
註冊您自己的AuthenticationSchemeSelector
。
基於提供的授權標頭 - 傳回Authentication
物件作為聲明(尚未經過身份驗證)
註冊您自己的支援MyAuthenticationObject
驗證的AuthenticationProvider
驗證提供的authentication
並傳回經過驗證和身份驗證的Authentication
對象
也可以透過在應用程式上下文中定義 Spring bean 來註冊AuthenticationSchemeSelector
:
@ Bean
public AuthenticationSchemeSelector myCustomSchemeSelector (){
return authHeader ->{
// your logic here
};
}
用戶端設定支援部分說明如何從 GRPC 用戶端傳遞自訂授權方案和聲明。
從版本4.5.9
開始,您也可以在 grpc 服務方法和 grpc 服務類型上使用標準@PreAuthorize
和@PostAuthorize
註解。
呼叫類型 | 輸入物件引用 | 輸出物件引用 | 樣本 |
---|---|---|---|
一元 | 按參數名稱 |
| @ Override
@ PreAuthorize ( "#person.age<12" )
@ PostAuthorize ( "returnObject.description.length()>0" )
public void unary ( Person person , StreamObserver < Assignment > responseObserver ) {
} |
輸入流, |
| | @ Override
@ PreAuthorize ( "#p0.getAge()<12" )
@ PostAuthorize ( "returnObject.description.length()>0" )
public StreamObserver < Person > inStream ( StreamObserver < Assignment > responseObserver ) {
} |
單一請求, | 按參數名稱 |
| @ Override
@ PreAuthorize ( "#person.age<12" )
@ PostAuthorize ( "returnObject.description.length()>0" )
public void outStream ( Person person , StreamObserver < Assignment > responseObserver ) {
} |
比迪河 |
| | @ Override
@ PreAuthorize ( "#p0.age<12" )
@ PostAuthorize ( "returnObject.description.length()>0" )
public StreamObserver < Person > bidiStream ( StreamObserver < Assignment > responseObserver ) {
} |
若要在安全方法的實作中取得Authentication
對象,請使用下列程式碼片段
final Authentication auth = GrpcSecurity . AUTHENTICATION_CONTEXT_KEY . get ();
從4.5.6
開始, Authentication
物件也可以透過標準Spring API取得:
final Authentication auth = SecurityContextHolder . getContext (). getAuthentication ();
透過將io.github.lognet:grpc-client-spring-boot-starter
依賴項新增至java grpc 用戶端應用程序,您可以輕鬆配置每個通道或每個呼叫的憑證:
class MyClient {
public void doWork (){
final AuthClientInterceptor clientInterceptor = new AuthClientInterceptor (( 1 )
AuthHeader . builder ()
. bearer ()
. binaryFormat ( true )( 3 )
. tokenSupplier ( this :: generateToken )( 4 )
);
Channel authenticatedChannel = ClientInterceptors . intercept (
ManagedChannelBuilder . forAddress ( "host" , 6565 ). build (), clientInterceptor ( 2 )
);
// use authenticatedChannel to invoke GRPC service
}
private ByteBuffer generateToken (){ ( 4 )
// generate bearer token against your resource server
}
}
建立客戶端攔截器
攔截通道
開啟/關閉二進位格式:
當true
時,驗證標頭將使用二進位編組器與Authorization-bin
鑰一起傳送。
當false
時,驗證標頭將使用 ASCII 編組器與Authorization
鑰一起傳送。
提供token生成器功能(請參考範例)
class MyClient {
public void doWork (){
AuthCallCredentials callCredentials = new AuthCallCredentials ( ( 1 )
AuthHeader . builder (). basic ( "user" , "pwd" . getBytes ())
);
final SecuredGreeterGrpc . SecuredGreeterBlockingStub securedFutureStub = SecuredGreeterGrpc . newBlockingStub ( ManagedChannelBuilder . forAddress ( "host" , 6565 ));( 2 )
final String reply = securedFutureStub
. withCallCredentials ( callCredentials )( 3 )
. sayAuthHello ( Empty . getDefaultInstance ()). getMessage ();
}
}
使用基本方案建立呼叫憑證
建立服務存根
將通話憑證附加到通話中
AuthHeader
也可以使用客製化的授權方案來建置:
AuthHeader
. builder ()
. authScheme ( "myCustomAuthScheme" )
. tokenSupplier (()-> generateMyCustomToken ())
啟動器註冊 HealthServiceImpl 的預設實作。
您可以透過在應用程式上下文中註冊 ManagedHealthStatusService bean 來提供自己的服務。
如果類別路徑中有org.springframework.boot:spring-boot-starter-actuator
和org.springframework.boot:spring-boot-starter-web
,啟動器會公開:
/actuator/health
端點下的grpc
健康狀況指標。
/actuator/grpc
端點。
這可以透過標準端點和運行狀況配置來控制。
從版本3.3.0
開始,如果org.springframework.cloud:spring-cloud-starter-consul-discovery
位於類別路徑中且spring.cloud.service-registry.auto-registration.enabled
starter 將在Consul 註冊表中自動註冊正在執行的spring.cloud.service-registry.auto-registration.enabled
false
。
如果您選擇同時執行嵌入式Grpc
和Web
伺服器,註冊的服務名稱將以grpc-
為前綴,即grpc-${spring.application.name}
以免干擾標準註冊的 Web 服務名稱。
ConsulDiscoveryProperties
從以spring.cloud.consul.discovery
為前綴的配置屬性綁定,然後由grpc.consul.discovery
前綴的屬性(如果設定)覆寫這些值。如果您選擇從應用程式中公開這兩個服務,這允許您為rest
和grpc
服務擁有單獨的consul發現配置。
spring :
cloud :
consul :
discovery :
metadata :
myKey : myValue (1)
tags :
- myWebTag (2)
grpc :
consul :
discovery :
tags :
- myGrpcTag (3)
rest
和grpc
服務都使用元資料myKey=myValue
註冊
Rest 服務已註冊到myWebTag
Grpc 服務註冊到myGrpcTag
將spring.cloud.consul.discovery.register-health-check
(或grpc.consul.discovery.register-health-check
)設定為true
將向 Consul 註冊 GRPC 健康檢查服務。
支援4種註冊模式:
SINGLE_SERVER_WITH_GLOBAL_CHECK
(預設)
在此模式下,正在執行的 grpc 伺服器會註冊為單一服務,並使用空serviceId
進行單一grpc
檢查。
請注意,預設實作不執行任何操作,只是傳回SERVING
狀態。您可能希望為此模式提供自訂運行狀況檢查實作。
SINGLE_SERVER_WITH_CHECK_PER_SERVICE
在此模式下,正在運行的 grpc 伺服器被註冊為單一服務,並根據每個發現的grpc
服務進行檢查。
STANDALONE_SERVICES
在此模式下,每個發現的 grpc 服務都註冊為單一服務並進行單一檢查。每個註冊的服務都由其自己的服務名稱標記。
NOOP
- 沒有註冊 grpc 服務。如果您在應用程式中同時提供rest
和grpc
服務,但由於某種原因,只有rest
服務應該向 Consul 註冊,則此模式非常有用。
grpc :
consule :
registration-mode : SINGLE_SERVER_WITH_CHECK_PER_SERVICE
在建立生產就緒服務時,建議為您的服務 gRPC API 建立單獨的項目,該項目僅包含用於伺服器端和用戶端使用的原始產生的類別。
然後,您將將此項目作為implementation
依賴項新增至您的gRPC client
和gRPC server
專案。
要整合Eureka
只需遵循 Spring 的優秀指南即可。
以下是伺服器和客戶端專案配置的基本部分。
將 eureka starter 新增為伺服器專案的依賴項以及從proto
檔案產生的類別:
dependencies {
implementation( ' org.springframework.cloud:spring-cloud-starter-eureka ' )
implementation project( " :yourProject-api " )
}
設定 gRPC 伺服器以向 Eureka 註冊自身。
spring :
application :
name : my-service-name (1)
Eureka的ServiceId
預設是spring應用程式名稱,在服務向Eureka註冊之前提供它。
grpc :
port : 6565 (1)
eureka :
instance :
nonSecurePort : ${grpc.port} (2)
client :
serviceUrl :
defaultZone : http://${eureka.host:localhost}:${eureka.port:8761}/eureka/ (3)
指定 gRPC 正在偵聽的連接埠號碼。
將 eureka 服務連接埠註冊為與grpc.port
相同,以便用戶端知道將請求傳送到哪裡。
指定註冊表 URL,以便服務將自行註冊。
將 gRPC 服務公開為 Spring Boot 應用程式的一部分。
@ SpringBootApplication
@ EnableEurekaClient
public class EurekaGrpcServiceApp {
@ GRpcService
public static class GreeterService extends GreeterGrpc . GreeterImplBase {
@ Override
public void sayHello ( GreeterOuterClass . HelloRequest request , StreamObserver < GreeterOuterClass . HelloReply > responseObserver ) {
}
}
public static void main ( String [] args ) {
SpringApplication . run ( DemoApp . class , args );
}
}
將 eureka starter 新增為客戶端專案的依賴項以及從proto
檔案產生的類別:
dependencies {
implementation( ' org.springframework.cloud:spring-cloud-starter-eureka ' )
implementation project( " :yourProject-api " )
}
設定客戶端尋找 eureka 服務註冊中心:
eureka :
client :
register-with-eureka : false (1)
service-url :
defaultZone : http://${eureka.host:localhost}:${eureka.port:8761}/eureka/ (2)
如果該專案不打算充當其他客戶的服務, false
。
指定註冊表 URL,以便該用戶端知道在哪裡尋找所需的服務。
@ EnableEurekaClient
@ SpringBootApplication
public class GreeterServiceConsumerApplication {
public static void main ( String [] args ) {
SpringApplication . run ( GreeterServiceConsumerApplication . class , args );
}
}
使用 EurekaClient 從 Eureka 取得 gRPC 服務實例的座標並消費該服務:
@ EnableEurekaClient
@ Component
public class GreeterServiceConsumer {
@ Autowired
private EurekaClient client ;
public void greet ( String name ) {
final InstanceInfo instanceInfo = client . getNextServerFromEureka ( "my-service-name" , false ); //(1)
final ManagedChannel channel = ManagedChannelBuilder . forAddress ( instanceInfo . getIPAddr (), instanceInfo . getPort ())
. usePlaintext ()
. build (); //(2)
final GreeterServiceGrpc . GreeterServiceFutureStub stub = GreeterServiceGrpc . newFutureStub ( channel ); //(3)
stub . greet ( name ); //(4)
}
}
取得有關my-service-name
實例的資訊。
相應地建立channel
。
使用channel
建立存根。
調用服務。
阿帕契2.0