스프링 부트 애플리케이션의 일부로 @GRpcService 지원 Bean을 사용하여 내장형 gRPC 서버를 자동 구성하고 실행합니다(짧은 비디오).
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
종속성을 사용해야 하는 경우 스타터는 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
Netty 라이브러리와 pure
Netty 라이브러리가 모두 있는 경우 생성해야 하는 NettyServerBuilder
유형을 선택하세요. 이는 GRpcServerBuilderConfigurer
(사용자 정의 gRPC 서버 구성 참조)에 전달될 유형이며, 기본값은 true
입니다(예: io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder
, false
인 경우 io.grpc.netty.NettyServerBuilder
).
스타터는 테스트 목적으로 사용해야 하는 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.BindableService
확장하는 생성된 io.grpc.examples.GreeterGrpc.GreeterImplBase
클래스를 참고하세요.
당신이 해야 할 일은 서비스 구현에 @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 ();
}
}
스타터는 Global 및 Per Service라는 두 종류의 인터셉터 등록을 지원합니다.
두 경우 모두 인터셉터는 io.grpc.ServerInterceptor
인터페이스를 구현해야 합니다.
서비스별
@ GRpcService ( interceptors = { LogInterceptor . class })
public class GreeterService extends GreeterGrpc . GreeterImplBase {
// ommited
}
LogInterceptor
유형의 Bean이 있는 경우 LogInterceptor
스프링 팩토리를 통해 인스턴스화되고, 그렇지 않으면 인수 없는 생성자를 통해 인스턴스화됩니다.
글로벌
@ 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(Fully Qualified Name)
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 서비스 메시지의 유효성을 검사하도록 스타터를 자동 구성할 수 있습니다. 구성 세부사항을 보려면 메시지 유효성 검증 구현을 계속하십시오.
스타터는 spring-cloud-stream
클래스 경로에 있을 때 함수 레지스트리로 고려되는 java.util.function.Consumer
유형의 Bean을 내부적으로 정의합니다. 이는 바람직하지 않습니다( spring-cloud-stream
정확히 채널이 있는 경우 채널을 자동 등록합니다). 애플리케이션 컨텍스트에 하나의 Consumer/Supplier/Function Bean이 있으므로 이 스타터를 spring-cloud-stream
과 함께 사용하면 이미 하나가 있는 것입니다.
이에 따라 프로덕션 준비 애플리케이션에서는 spring.cloud.function.definition
속성을 사용하고 자동 검색에 의존하지 않는 것이 좋습니다.
GRPC Kafka Stream 데모를 참조하세요. 필수 부분은 이 줄입니다.
스타터는 Spring Security 프레임워크와의 통합을 활용하여 사용자를 인증하고 권한을 부여하기 위한 기본 지원을 제공합니다.
지원되는 인증 공급자 및 구성 옵션에 대한 자세한 내용은 Spring Security Integration 섹션을 참조하세요.
개인 키 경로와 함께 루트 인증서를 사용하여 전송 보안을 구성할 수 있습니다.
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 ());
이 스타터는 SSL을 지원하기 위해 기본적으로 io.netty:netty-tcnative-boringssl-static
종속성을 가져옵니다.
다른 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
는 서버 시작 시 게시되며 일반 스프링 API를 사용하여 사용할 수 있습니다.
버전 5.1.0
부터 spring-boot-starter-gradle-plugin은 SalesForce의 반응성-grpc 프로토콜 플러그인을 통합합니다.
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
를 등록합니다.
오류 처리 방법은 @GRpcExceptionHandler
주석이 달린 메서드와 함께 @GRpcServiceAdvice
주석이 달린 빈을 사용하여 등록할 수 있습니다.
이들은 global
오류 핸들러로 간주되며 유형 계층 구조에서 발생한 예외에 가장 가까운 예외 유형 매개변수가 있는 메소드가 호출됩니다.
오류 처리기의 서명은 아래 패턴을 따라야 합니다.
반환 유형 | 매개변수 1 | 매개변수 2 |
---|---|---|
io.grpc.상태 | 모든 | GRpcExceptionScope |
@ 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
빈과 핸들러 메소드를 가질 수 있습니다.
grpc
서비스 빈은 @GRpcServiceAdvice
빈에서 발견된 전역 오류 처리 방법보다 우선순위가 높은 오류 처리기에 대해서도 발견됩니다. 서비스 수준 오류 처리 메서드는 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
예외가 발생하는 경우 handler
메서드의 scope
개체에서 액세스할 수 있는 hint
개체를 전달할 수도 있습니다.
런타임 예외는 있는 그대로 발생할 수 있으며 래핑할 필요가 없습니다.
힌트 개체를 얻습니다.
클라이언트에 사용자 정의 헤더를 보냅니다.
인증 실패는 AuthenticationException
통해 전파되고 인증 실패는 AccessDeniedException
을 통해 전파됩니다.
유효성 검사 실패는 ConstraintViolationException
통해 전파됩니다. 실패한 요청의 경우 - Status.INVALID_ARGUMENT
힌트로 사용하고, 실패한 응답의 경우 - Status.FAILED_PRECONDITION
힌트로 사용합니다.
데모는 여기에 있습니다
XML 배포 설명자를 통한 Bean 유효성 검사 구성 지원 덕분에 생성된 메시지를 사용자 정의 protoc
컴파일러로 계측하는 대신 XML을 통해 생성된 클래스에 대한 제약 조건을 제공하는 것이 가능합니다.
프로젝트에 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.PERMISSION_DENIED/Status.UNAUTHENTICATED
응답 상태 대신 Status.INVALID_ARGUMENT
받게 됩니다. 데모는 여기
@Transactional
(기본적으로 활성화되어 있지 않은 경우 spring.aop.proxy-target-class=true
사용)으로 주석이 달린 rpc 메소드를 갖는 것이 여전히 가능하지만 예측할 수 없는 동작이 발생할 가능성이 있습니다. 아래 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 )
}
}
또는 @Secured
주석과 API
결합하세요.
이 기본 구성은 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
객체를 반환합니다.
AuthenticationSchemeSelector
애플리케이션 컨텍스트에서 Spring 빈을 정의하여 등록할 수도 있습니다.
@ 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
부터 표준 Spring API를 통해 Authentication
객체를 얻을 수도 있습니다.
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
키와 함께 인증 헤더가 전송됩니다.
토큰 생성 기능 제공(예시를 참고하세요.)
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 빈을 등록하여 자신만의 서비스를 제공할 수 있습니다.
클래스 경로에 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
classpath 및 spring.cloud.service-registry.auto-registration.enabled
에 있는 경우 스타터는 Consul 레지스트리에서 실행 중인 grpc 서버를 자동 등록합니다. spring.cloud.service-registry.auto-registration.enabled
false
로 설정되지 않았습니다 .
등록된 서비스 이름에는 grpc-
, 즉 grpc-${spring.application.name}
접두사가 추가되어 내장된 Grpc
및 Web
서버를 모두 실행하도록 선택한 경우 표준 등록 웹 서비스 이름을 방해하지 않습니다.
ConsulDiscoveryProperties
spring.cloud.consul.discovery
접두사가 붙은 구성 속성에서 바인딩된 다음 grpc.consul.discovery
붙은 속성(설정된 경우)으로 값을 덮어씁니다. 이를 통해 애플리케이션에서 둘 다 노출하도록 선택한 경우 rest
및 grpc
서비스에 대해 별도의 영사 검색 구성을 가질 수 있습니다.
spring :
cloud :
consul :
discovery :
metadata :
myKey : myValue (1)
tags :
- myWebTag (2)
grpc :
consul :
discovery :
tags :
- myGrpcTag (3)
rest
및 grpc
서비스는 모두 myKey=myValue
메타데이터로 등록됩니다.
휴식 서비스는 myWebTag
에 등록됩니다.
Grpc 서비스는 myGrpcTag
에 등록되어 있습니다.
spring.cloud.consul.discovery.register-health-check
(또는 grpc.consul.discovery.register-health-check
)를 true
로 설정하면 GRPC 상태 확인 서비스가 Consul에 등록됩니다.
지원되는 등록 모드는 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에 대한 별도의 프로젝트를 갖는 것이 좋습니다.
그런 다음 이 프로젝트를 gRPC client
및 gRPC server
프로젝트에 대한 implementation
종속성으로 추가합니다.
Eureka
통합하려면 Spring의 훌륭한 가이드를 따르십시오.
다음은 서버 및 클라이언트 프로젝트 구성의 필수 부분입니다.
proto
파일에서 생성된 클래스와 함께 서버 프로젝트의 종속성으로 유레카 스타터를 추가합니다.
dependencies {
implementation( ' org.springframework.cloud:spring-cloud-starter-eureka ' )
implementation project( " :yourProject-api " )
}
Eureka에 등록되도록 gRPC 서버를 구성합니다.
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가 수신 대기 중인 포트 번호를 지정합니다.
유레카 서비스 포트를 grpc.port
와 동일하게 등록하면 클라이언트가 요청을 보낼 위치를 알 수 있습니다.
서비스가 자체적으로 등록되도록 레지스트리 URL을 지정하십시오.
Spring Boot 애플리케이션의 일부로 gRPC 서비스를 노출합니다.
@ 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 );
}
}
proto
파일에서 생성된 클래스와 함께 클라이언트 프로젝트의 종속성으로 유레카 스타터를 추가합니다.
dependencies {
implementation( ' org.springframework.cloud:spring-cloud-starter-eureka ' )
implementation project( " :yourProject-api " )
}
유레카 서비스 레지스트리를 찾도록 클라이언트를 구성합니다.
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