gRPC Spring Boot StartergRPC 框架的 Spring Boot 啟動(dòng)器模塊
gRPC 框架的 Spring Boot 啟動(dòng)器模塊
項(xiàng)目主頁(yè):grpc-spring-boot-starter
特點(diǎn)
-
使用
@ GrpcService自動(dòng)創(chuàng)建并運(yùn)行一個(gè) gRPC 服務(wù),內(nèi)嵌在 spring-boot 應(yīng)用中 -
使用
@ GrpcClient自動(dòng)創(chuàng)建和管理你的channel和stub -
支持 Spring Cloud(向 Consul 或 Eureka 或 Nacos 注冊(cè)服務(wù)并獲取gRPC服務(wù)信息)
-
支持 Spring Sleuth 進(jìn)行鏈路跟蹤(需要單獨(dú)引入 brave-instrumentation-grpc)
-
支持對(duì) server、client 分別設(shè)置全局?jǐn)r截器或單個(gè)的攔截器
-
支持 metric (micrometer / actuator)
-
可以使用 (non-shaded) grpc-netty
版本
2.x.x.RELEASE 支持 Spring Boot 2.1+ & Spring Cloud Greenwich。
最新的版本:2.5.0.RELEASE
(使用 2.4.0.RELEASE 版本可以支持 Spring Boot 2.0.X & Spring Cloud Finchley).
1.x.x.RELEASE 支持 Spring Boot 1 & Spring Cloud Edgware 、Dalston、Camden。
最新的版本:1.4.2.RELEASE
注意: 此項(xiàng)目也可以在沒(méi)有 Spring-Boot 的場(chǎng)景下使用,但需要手動(dòng)的配置相關(guān)的 bean。
使用方式
gRPC server + client
如果使用的是 Maven,添加如下依賴(lài)
<dependency> <groupId>net.devh</groupId> <artifactId>grpc-spring-boot-starter</artifactId> <version>2.5.0.RELEASE</version> </dependency>
如果使用的 Gradle,添加如下依賴(lài)
dependencies {
compile 'net.devh:grpc-spring-boot-starter:2.5.0.RELEASE'
}
gRPC 服務(wù)端
如果使用的是 Maven,添加如下依賴(lài)
<dependency> <groupId>net.devh</groupId> <artifactId>grpc-server-spring-boot-starter</artifactId> <version>2.5.0.RELEASE</version> </dependency>
如果使用的 Gradle,添加如下依賴(lài)
dependencies {
compile 'net.devh:grpc-server-spring-boot-starter:2.5.0.RELEASE'
}
實(shí)現(xiàn) gRPC server 的業(yè)務(wù)邏輯,并使用 @GrpcService 注解
@GrpcService
public class GrpcServerService extends GreeterGrpc.GreeterImplBase {
@Override
public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
HelloReply reply = HelloReply.newBuilder().setMessage("Hello ==> " + req.getName()).build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
}
}
設(shè)置 gRPC 的 host 跟 port ,默認(rèn)的監(jiān)聽(tīng)的 port 是 9090。其他配置屬性可以參考 settings。 所有的配置屬性在 server 中使用需增加 grpc.server. 的前綴
服務(wù)端配置屬性示例
grpc.server.port=9090 grpc.server.address=0.0.0.0 #grpc.server.inProcessName=test
對(duì) Server 進(jìn)行自定義
當(dāng)前項(xiàng)目同樣支持對(duì) ServerBuilder 的自定義修改,需要在創(chuàng)建的過(guò)程中使用 GrpcServerConfigurer beans。
@Bean
public GrpcServerConfigurer keepAliveServerConfigurer() {
return serverBuilder -> {
if (serverBuilder instanceof NettyServerBuilder) {
((NettyServerBuilder) serverBuilder)
.keepAliveTime(30, TimeUnit.SECONDS)
.keepAliveTimeout(5, TimeUnit.SECONDS)
.permitKeepAliveWithoutCalls(true);
}
};
}
Server-Security
支持使用 Spring-Security 加密你的 gRPC 應(yīng)用。你只需要添加 Spring-Security(core 或者 config)依賴(lài),然后根據(jù)需要再增加加密的配置
首先需要選擇一個(gè)認(rèn)證方案
-
BasicAuth(基礎(chǔ)認(rèn)證)
@Bean AuthenticationManager authenticationManager() { final List<AuthenticationProvider> providers = new ArrayList<>(); providers.add(...); // Possibly DaoAuthenticationProvider return new ProviderManager(providers); } @Bean GrpcAuthenticationReader authenticationReader() { final List<GrpcAuthenticationReader> readers = new ArrayList<>(); readers.add(new BasicGrpcAuthenticationReader()); return new CompositeGrpcAuthenticationReader(readers); } -
Bearer Authentication (OAuth2/OpenID-Connect)
@Bean AuthenticationManager authenticationManager() { final List<AuthenticationProvider> providers = new ArrayList<>(); providers.add(...); // Possibly JwtAuthenticationProvider return new ProviderManager(providers); } @Bean GrpcAuthenticationReader authenticationReader() { final List<GrpcAuthenticationReader> readers = new ArrayList<>(); readers.add(new BearerAuthenticationReader(accessToken -> new BearerTokenAuthenticationToken(accessToken))); return new CompositeGrpcAuthenticationReader(readers); }你可能還想定義自己的 GrantedAuthoritiesConverter ,將權(quán)限和角色的信息映射到 Spring Security 的
GrantedAuthority中 -
Certificate Authentication(證書(shū)認(rèn)證)
@Bean AuthenticationManager authenticationManager() { final List<AuthenticationProvider> providers = new ArrayList<>(); providers.add(new X509CertificateAuthenticationProvider(userDetailsService())); return new ProviderManager(providers); } @Bean GrpcAuthenticationReader authenticationReader() { final List<GrpcAuthenticationReader> readers = new ArrayList<>(); readers.add(new SSLContextGrpcAuthenticationReader()); return new CompositeGrpcAuthenticationReader(readers); }相關(guān)的配置屬性如下:
grpc.server.security.enabled=true grpc.server.security.certificateChain=file:certificates/server.crt grpc.server.security.privateKey=file:certificates/server.key grpc.server.security.trustCertCollection=file:certificates/trusted-clients-collection grpc.server.security.clientAuth=REQUIRE
-
使用
CompositeGrpcAuthenticationReader類(lèi)鏈?zhǔn)降恼{(diào)用多個(gè)認(rèn)證方案 -
自定義認(rèn)證方式(繼承并實(shí)現(xiàn)
GrpcAuthenticationReader類(lèi))
如何去保護(hù)你的這些服務(wù)
-
使用 Spring-Security 的注解
@Configuration @EnableGlobalMethodSecurity(proxyTargetClass = true, ...) public class SecurityConfiguration {如果你想使用 Spring Security 相關(guān)的注解的話(huà),
proxyTargetClass屬性是必須的! 但是你會(huì)受到一條警告,提示 MyServiceImpl#bindService() 方式是用 final 進(jìn)行修飾的。 這條警告目前無(wú)法避免,但它是安全的,可以忽略它。 -
手動(dòng)配置
@Bean AccessDecisionManager accessDecisionManager() { final List<AccessDecisionVoter<?>> voters = new ArrayList<>(); voters.add(new AccessPredicateVoter()); return new UnanimousBased(voters); } @Bean GrpcSecurityMetadataSource grpcSecurityMetadataSource() { final ManualGrpcSecurityMetadataSource source = new ManualGrpcSecurityMetadataSource(); source.set(MyServiceGrpc.getSecureMethod(), AccessPredicate.hasRole("ROLE_USER")); source.setDefault(AccessPredicate.permitAll()); return source; }
gRPC 客戶(hù)端
如果使用的是 Maven,添加如下依賴(lài)
<dependency> <groupId>net.devh</groupId> <artifactId>grpc-client-spring-boot-starter</artifactId> <version>2.5.0.RELEASE</version> </dependency>
如果使用的 Gradle,添加如下依賴(lài)
dependencies {
compile 'net.devh:grpc-client-spring-boot-starter:2.5.0.RELEASE'
}
這里有三種方式去或得一個(gè)gRPC server的連接
-
使用
grpcChannelFactory.createChannel(serverName)去創(chuàng)建一個(gè)Channel,并創(chuàng)建一個(gè)自己的 gRPC stub.@Autowired private GrpcChannelFactory grpcChannelFactory; private GreeterGrpc.GreeterBlockingStub greeterStub; @PostConstruct public void init() { Channel channel = grpcChannelFactory.createChannel("gRPC server name"); greeterStub = GreeterGrpc.newBlockingStub(channel); } -
通過(guò)在
Channel類(lèi)型的字段上加入@GrpcClient(serverName)注解,并創(chuàng)建一個(gè)自己的 gRPC stub.- 不需要使用
@Autowired或者@Inject來(lái)進(jìn)行注入
@GrpcClient("gRPC server name") private Channel channel; private GreeterGrpc.GreeterBlockingStub greeterStub; @PostConstruct public void init() { greeterStub = GreeterGrpc.newBlockingStub(channel); } - 不需要使用
-
直接將
@GrpcClient(serverName)注解加在調(diào)用客戶(hù)端的 stub 上- 不需要使用
@Autowired或者@Inject來(lái)進(jìn)行注入
@GrpcClient("gRPC server name") private GreeterGrpc.GreeterBlockingStub greeterStub; - 不需要使用
注意: 你可以為多個(gè) channels 和多個(gè)不同的 stubs 使用相同的 serverName (除非他們攔截器不一樣).
然后你可以直接向服務(wù)端發(fā)起請(qǐng)求,如下:
HelloReply response = stub.sayHello(HelloRequest.newBuilder().setName(name).build());
可以單獨(dú)為每一個(gè) client 配置對(duì)應(yīng)的 address 但在某些情況下,你可以調(diào)整默認(rèn)的配置。 你可以通過(guò) NameResolver.Factory beans 去自定義默認(rèn)的 url 映射,如果你沒(méi)有配置這個(gè) bean,那將會(huì)按照下面的方式進(jìn)行解析:
- 如果存在一個(gè)
DiscoveryClient的 bean,這時(shí)會(huì)使用 client name 去注冊(cè)中心上進(jìn)行獲取對(duì)應(yīng)服務(wù)的 address - 否則 client 端將使用
localhost和9090端口
其他的配置屬性參考 settings,所有的配置文件在 client 端使用時(shí)需要增加 grpc.client.(serverName).的前綴
你也可以配置多個(gè)目標(biāo)地址,請(qǐng)求時(shí)會(huì)自動(dòng)使用負(fù)載均衡
static://127.0.0.1:9090,[::1]:9090
你也可以使用服務(wù)發(fā)現(xiàn)去獲取目標(biāo)地址(要求一個(gè) DiscoveryClient bean)
discovery:///my-service-name
此外,你也可以使用 DNS 的方式去獲取目標(biāo)地址
dns:///example.com
同時(shí),你也可以使用如下方式直接進(jìn)程內(nèi)訪問(wèn)
in-process:test
它會(huì)通過(guò)DNS將域名解析出所有真實(shí)的 IP 地址,通過(guò)使用這些真實(shí)的IP地址去做負(fù)載均衡。 需要注意的是 grpc-java 出于性能的考慮對(duì) DNS 返回的結(jié)果做緩存。 有關(guān)這些和其他原生支持的 NameResolverProviders 參考官方文檔 grpc-java sources
客戶(hù)端配置屬性示例
grpc.client.GLOBAL.enableKeepAlive=true grpc.client.(gRPC server name).address=static://localhost:9090 # Or grpc.client.myName.address=static://localhost:9090
GLOBAL 是一個(gè)特殊的常量,它可以用于對(duì)所有 Client 統(tǒng)一的設(shè)置屬性。 屬性覆蓋的順序:Client單獨(dú)的屬性 > GLOBAL的屬性 > 默認(rèn)的屬性
自定義 Client
This library also supports custom changes to the ManagedChannelBuilder and gRPC client stubs during creation by creating GrpcChannelConfigurer and StubTransformer beans. 當(dāng)前項(xiàng)目支持對(duì) ManagedChannelBuilder 的自定義,在 gRPC client stub創(chuàng)建的過(guò)程中,通過(guò)使用 GrpcChannelConfigurer 或 StubTransformer bean 來(lái)完成自定義操作
@Bean
public GrpcChannelConfigurer keepAliveClientConfigurer() {
return (channelBuilder, name) -> {
if (channelBuilder instanceof NettyChannelBuilder) {
((NettyChannelBuilder) channelBuilder)
.keepAliveTime(15, TimeUnit.SECONDS)
.keepAliveTimeout(5, TimeUnit.SECONDS);
}
};
}
@Bean
public StubTransformer authenticationStubTransformer() {
return (clientName, stub) -> stub.withCallCredentials(grpcCredentials(clientName));
}
客戶(hù)端認(rèn)證
注意: 以下列出的一些方法僅僅適用于通過(guò)注入得到的 stubs,如果你通過(guò)注入 Channel,手動(dòng)的在去創(chuàng)建 stubs,這就需要你自己手動(dòng)的 去配置憑證。然而你同樣能從目前所提供的一些輔助類(lèi)方法中收益。
客戶(hù)端有許多不同的認(rèn)證方式,我們只需定義一個(gè)類(lèi)型為 CallCredentials 的 bean,它會(huì)自動(dòng)作用于身份驗(yàn)證。目前通過(guò)一些輔助方法可以支持 下列的認(rèn)證方式:
-
BasicAuth
@Bean CallCredentials grpcCredentials() { return CallCredentialsHelper.basicAuth(username, password); } -
Bearer Authentication (OAuth2, OpenID-Connect)
@Bean CallCredentials grpcCredentials() { return CallCredentialsHelper.bearerAuth(token); } -
Certificate Authentication
需要一些配置屬性:
#grpc.client.test.security.authorityOverride=localhost #grpc.client.test.security.trustCertCollection=file:certificates/trusted-servers-collection grpc.client.test.security.clientAuthEnabled=true grpc.client.test.security.certificateChain=file:certificates/client.crt grpc.client.test.security.privateKey=file:certificates/client.key
-
為每個(gè) client 使用不同的認(rèn)證
通過(guò)定義一個(gè)
StubTransformerbean 來(lái)代替原有的CallCredentialsbean@Bean StubTransformer grpcCredentialsStubTransformer() { return CallCredentialsHelper.mappedCredentialsStubTransformer( Map.of( clientA, callCredentialsAC, clientB, callCredentialsB, clientC, callCredentialsAC)); }注意: 如果你配置了
CallCredentialsbean,然后再使用StubTransformer的話(huà)可能會(huì)造成沖突。
使用 (non-shaded)grpc-netty
當(dāng)前項(xiàng)目目前支持 grpc-netty 和 grpc-netty-shaded。 使用 grpc-netty-shaded 可以防止 grpc 跟 netty 版本的兼容性問(wèn)題。
注意: 如果 grpc-netty-shaded 已經(jīng)存在于 classpath 中, 那么將優(yōu)先使用 shaded-netty
如果你使用的Maven,你可以使用如下的配置:
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
<version>${grpcVersion}</version>
</dependency>
<!-- For both -->
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-spring-boot-starter</artifactId>
<version>...</version>
<exclusions>
<exclusion>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- For the server (only) -->
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-server-spring-boot-starter</artifactId>
<version>...</version>
<exclusions>
<exclusion>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- For the client (only) -->
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-client-spring-boot-starter</artifactId>
<version>...</version>
<exclusions>
<exclusion>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
</exclusion>
</exclusions>
</dependency>
如果你使用的 Gradle,你可以使用如下的配置:
compile "io.grpc:grpc-netty:${grpcVersion}"
compile 'net.devh:grpc-spring-boot-starter:...' exclude group: 'io.grpc', module: 'grpc-netty-shaded' // For both
compile 'net.devh:grpc-client-spring-boot-starter:...' exclude group: 'io.grpc', module: 'grpc-netty-shaded' // For the client (only)
compile 'net.devh:grpc-server-spring-boot-starter:...' exclude group: 'io.grpc', module: 'grpc-netty-shaded' // For the server (only)
