Skip to content

Latest commit

 

History

History
931 lines (799 loc) · 34.7 KB

File metadata and controls

931 lines (799 loc) · 34.7 KB

logo Spring Boot Starter for gRPC

Sun Qian
fredsuvn@163.com

Table of Contents

Introduction

This is a concise, clear, easy to extend and high-customizable gRPC spring-boot starter, with spring-boot style.

It needs Spring Boot 2+ and JDK1.8+.

Features

  • Simply use @GrpcService, @GrpcServerInterceptor and spring bean annotation such as @Component to register gRPC service and server interceptors;

  • Simply use @GrpcClient, @GrpcClientInterceptor and spring bean annotation such as @Component to register gRPC stub, channel and client interceptors;

  • Support multi-servers;

  • Provide simple way to client load-balance;

  • Fine-grained control for servers/clients and service and interceptors.

Getting

Gradle

Get grpc-spring-boot:

implementation("xyz.srclab.grpc.spring.boot:grpc-spring-boot-starter-server:0.0.1")
implementation("xyz.srclab.grpc.spring.boot:grpc-spring-boot-starter-client:0.0.1")
implementation("xyz.srclab.grpc.spring.boot:grpc-spring-boot-starter-web:0.0.1")

Use bom:

api platform("xyz.srclab.grpc.spring.boot:grpc-spring-boot-starter-bom:0.0.1")

Maven

Get grpc-spring-boot:

<dependencies>
  <dependency>
    <groupId>xyz.srclab.grpc.spring.boot</groupId>
    <artifactId>grpc-spring-boot-starter-server</artifactId>
    <version>0.0.1</version>
  </dependency>
  <dependency>
    <groupId>xyz.srclab.grpc.spring.boot</groupId>
    <artifactId>grpc-spring-boot-starter-client</artifactId>
    <version>0.0.1</version>
  </dependency>
  <dependency>
    <groupId>xyz.srclab.grpc.spring.boot</groupId>
    <artifactId>grpc-spring-boot-starter-web</artifactId>
    <version>0.0.1</version>
  </dependency>
</dependencies>

Use bom:

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>xyz.srclab.grpc.spring.boot</groupId>
      <artifactId>grpc-spring-boot-starter-bom</artifactId>
      <version>0.0.1</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

Source Code

https://github.com/srclab-projects/grpc-spring-boot

Samples

grpc-spring-boot-samples

Usage

Server

Create and start a server

To create a gRPC server, first we add server properties in application.yml (or .yaml, .properties):

grpc:
  server:
    servers:
      server1:
        host: 127.0.0.1
        port: 6565

Now we have a gRPC server called server1 with address: 127.0.0.1:6565. Then we add service on server1:

@Service
public class DefaultHelloService extends DefaultHelloServiceGrpc.DefaultHelloServiceImplBase {

    @Override
    public void hello(HelloRequest request, StreamObserver<HelloResponse> responseObserver) {
        responseObserver.onNext(HelloResponse.newBuilder()
            .setMessage("DefaultHelloService")
            .setThreadName(Thread.currentThread().getName())
            .build()
        );
        responseObserver.onCompleted();
    }
}

Now server1 has a gRPC service DefaultHelloService. If we run the application, server1 will be auto-start.

Multi-Servers

If we need two servers that one on 6565 and another on 6566, and they share the host localhost:

grpc:
  server:
    defaults:
      host: 127.0.0.1
    servers:
      server1:
        port: 6565
      server2:
        port: 6566

defaults property has same properties with each server property. server properties will auto-inherit defaults properties which is not overridden.

@GrpcService

By default, if a gRPC service class is annotated by @Service or other spring-boot component annotation, it will work for all servers. Thus, DefaultHelloService will work for both server1 and server2. If we want DefaultHelloService only works for server1:

@GrpcService("server1")
public class DefaultHelloService{}

@GrpcService(serverPatterns = "server1")
public class DefaultHelloService{}

@GrpcService(serverPatterns = "*1")
public class DefaultHelloService{}

@GrpcService can specify the servers which gRPC service works for, by bean name declared on value or serverPatterns, and it supports ant-pattern. Now DefaultHelloService only works for server1.

@GrpcServerInterceptor

Adding server interceptor is same with adding gRPC server:

@Component
public class DefaultServerInterceptor extends BaseServerInterceptor {

    @Override
    public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
        ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
        if (Objects.equals(call.getMethodDescriptor().getServiceName(), "HelloService2")) {
            helloService2.addInterceptorTrace("DefaultServerInterceptor");
        }
        return super.interceptCall(call, headers, next);
    }
}

DefaultServerInterceptor will work for all gRPC services (DefaultHelloService), to limit it, use @GrpcServerInterceptor:

@GrpcServerInterceptor(value = "*hello*", order = -2)
public class DefaultServerInterceptor{}

@GrpcServerInterceptor(servicePatterns = "*hello*", order = -3)
public class DefaultServerInterceptor{}

Just like @GrpcService, @GrpcServerInterceptor can specify service bean name pattern and support ant-pattern. The order property specifies callback order, from low to high. Now DefaultServerInterceptor only works for gRPC service whose bean name matches *hello*.

AbstractServerInterceptor and SimpleServerInterceptor

ServerInterceptor is confusing (think about its nested calling, callback execution order). For convenience this starter provides AbstractServerInterceptor and SimpleServerInterceptor.

AbstractServerInterceptor is a skeletal implementation of ServerInterceptor, provides serials of callback methods to override, in simple order: intercept1 → intercept2 → onMessage2 → onMessage1 (detail see its javadoc).

SimpleServerInterceptor is an interface provides serials of callback methods to override same with AbstractServerInterceptor.

Difference:

  • Each AbstractServerInterceptor is a ServerInterceptor instance but all SimpleServerInterceptor in a gRPC service will be merged to one ServerInterceptor;

  • Callback order is: intercept1 → intercept2 → onMessage1 → onMessage2 (detail see its javadoc).

MetadataServerInterceptor

MetadataServerInterceptor is a simple ServerInterceptor to do with metadata (headers).

DefaultGrpcServerConfigurer and DefaultGrpcServerConfigureHelper

By default, this starter uses InProcessBuilder, NettyServerBuilder and ShadedNettyServerBuilder to create new gRPC server. If you want to custom them, create a new bean of DefaultGrpcServerConfigurer and use bean DefaultGrpcServerConfigureHelper to help.

GrpcServerFactory and DefaultGrpcServerFactory

This starter uses GrpcServerFactory to create a new gRPC server, its default implementation is DefaultGrpcServerFactory. If you want to custom this process, create a new bean of GrpcServerFactory to instead.

Note
DefaultGrpcServerConfigurer will invalid if you have a custom GrpcServerFactory bean, but DefaultGrpcServerConfigureHelper can be used still.

GrpcServersFactory and DefaultGrpcServersFactory

This starter uses GrpcServersFactory to create all gRPC server, its default implementation is DefaultGrpcServersFactory. If you want to custom this process, create a new bean of GrpcServersFactory to instead.

Note
DefaultGrpcServerFactory and DefaultGrpcServerConfigurer will invalid if you have a custom GrpcServersFactory bean, but DefaultGrpcServerConfigureHelper can be used still.

Server Configuration Properties Table

Table 1. GrpcServersProperties
Key Type Default Comment

defaults

ServerProperties

servers

Map<String, ServerProperties>

needGrpcAnnotation

Boolean

false

Whether gRPC bean (BindableService and ServerInterceptor) should be annotated by gRPC annotation (GrpcService and GrpcServerInterceptor).

This means spring-boot annotation such as @Component is invalid for gRPC bean.

Default is false.

Table 1. GrpcServersProperties

Table 2. ServerProperties
Key Type Default Comment

inProcess

Boolean

false

useShaded

Boolean

false

host

String

localhost

port

Int

6565

threadPoolBeanName

String

Thread pool bean name for gRPC executor.

maxConcurrentCallsPerConnection

Int

initialFlowControlWindow

Int

flowControlWindow

Int

maxMessageSize

Int

maxHeaderListSize

Int

keepAliveTimeInNanos

Long

keepAliveTimeoutInNanos

Long

maxConnectionIdleInNanos

Long

maxConnectionAgeInNanos

Long

maxConnectionAgeGraceInNanos

Long

permitKeepAliveWithoutCalls

Boolean

permitKeepAliveTimeInNanos

Long

sslCertChainClassPath

String

Same classpath and file properties are alternative and classpath first

sslPrivateKeyClassPath

String

Same classpath and file properties are alternative and classpath first

sslTrustCertCollectionClassPath

String

Same classpath and file properties are alternative and classpath first

sslCertChainFile

String

Same classpath and file properties are alternative and classpath first

sslPrivateKeyFile

String

Same classpath and file properties are alternative and classpath first

sslTrustCertCollectionFile

String

Same classpath and file properties are alternative and classpath first

sslPrivateKeyPassword

String

sslClientAuth

String

Auth enum with case-ignore: none, optional or require.

Default is none.

Table 2. ServerProperties

Client

Create a Client

To create a gRPC client, first we add client properties in application.yml (or .yaml, .properties):

grpc:
  client:
    clients:
      client1:
        target: 127.0.0.1:6565

Now we have a gRPC client called client1 with target: 127.0.0.1:6565. Then we add stub and channel on client1:

public class TestBean {

    @GrpcClient
    private DefaultHelloServiceGrpc.DefaultHelloServiceBlockingStub stub1;

    @GrpcClient
    private Channel channel1;
}

Now, stub1 and channel1 will be auto-wired with client1's properties when application starts.

Multi-Clients:

If we need two clients, for target 127.0.0.1:6565 and 127.0.0.1:6566:

grpc:
  client:
    clients:
      client1:
        target: 127.0.0.1:6565
      client2:
        target: 127.0.0.1:6566

Then:

public class TestBean {

    @GrpcClient
    private DefaultHelloServiceGrpc.DefaultHelloServiceBlockingStub defaultStub;

    @GrpcClient("client1")
    private HelloServiceXGrpc.HelloServiceXBlockingStub client1Stub;

    @GrpcClient("client2")
    private HelloService2Grpc.HelloService2BlockingStub client2Stub;
}

If no client name specified on @GrpcClient, it will auto-wired with first client name (here is client1).

Note
Client configuration also inherit defaults properties like Multi-Servers.

Load Balance

To set a load-balance target:

grpc:
  client:
    clients:
      lb:
        target: lb:127.0.0.1/127.0.0.1:6666,127.0.0.1/127.0.0.1:6667

Now the client lb is load-balance.

Note
load balance syntax is: lb:authority1/host1:port1,authority2/host2:port2…​

ClientInterceptor

To declare a ClientInterceptor, just give a bean of ClientInterceptor type:

@Component
public class DefaultClientInterceptor extends BaseClientInterceptor {

    @Override
    public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
        MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
        if (Objects.equals(method.getServiceName(), "HelloService2")) {
            traceService.addInterceptorTrace("DefaultClientInterceptor");
        }
        return super.interceptCall(method, callOptions, next);
    }
}

Now we have a DefaultClientInterceptor as ClientInterceptor for all client.

@ClientInterceptor

To specify interceptor work in fine-grained, use @GrpcServerInterceptor:

@GrpcClientInterceptor(value = "*2", order = 0)
public class DefaultClientInterceptor{}

@GrpcClientInterceptor(clientPatterns = "*2", order = -3)
public class DefaultClientInterceptor{}

value or clientPatterns specifies which client DefaultClientInterceptor work for, support ant-pattern. For now, it only works for client whose bean name matches \*2.

AbstractClientInterceptor and SimpleClientInterceptor

ClientInterceptor is confusing (think about its nested calling, callback execution order). For convenience this starter provides AbstractClientInterceptor and SimpleClientInterceptor.

AbstractClientInterceptor is a skeletal implementation of ClientInterceptor, provides serials of callback methods to override, in simple order: intercept1 → intercept2 → onClose2 → onClose1 (detail see its javadoc).

SimpleClientInterceptor is an interface provides serials of callback methods to override same with AbstractClientInterceptor.

Difference:

  • Each AbstractClientInterceptor is a ClientInterceptor instance but all SimpleClientInterceptor in a gRPC channel will be merged to one ClientInterceptor;

  • Callback order is: intercept1 → intercept2 → onClose1 → onClose2 (detail see its javadoc).

DefaultGrpcChannelConfigurer and DefaultGrpcChannelConfigureHelper

By default, this starter uses InProcessBuilder, NettyServerBuilder and ShadedNettyServerBuilder, if you want to custom them, create a new bean of DefaultGrpcChannelConfigurer and use bean DefaultGrpcChannelConfigureHelper to help.

GrpcChannelFactory, DefaultGrpcChannelFactory, GrpcStubFactory and DefaultGrpcStubFactory

This starter uses GrpcChannelFactory to create a new gRPC stub, use GrpcStubFactory to create a new gRPC channel. Default implementation is DefaultGrpcChannelFactory and DefaultGrpcStubFactory. If you want to custom this process, create a new bean of GrpcChannelFactory or GrpcStubFactory.

Note
DefaultGrpcChannelConfigurer will invalid if you have a custom GrpcChannelFactory bean, but DefaultGrpcChannelConfigureHelper can be used still.

GrpcTargetResolver and DefaultGrpcTargetResolver

This starter will register LbNameResolverProvider to resolve load balance target (lb:authority/host1:port1,host2:port2…​). By default, LbNameResolverProvider use DefaultGrpcTargetResolver to resolve, to custom this process, create bean of GrpcTargetResolver to instead.

Client Configuration Properties Table

Table 3. GrpcClientsProperties
Key Type Default Comment

defaults

ClientProperties

servers

Map<String, ClientProperties>

needGrpcAnnotation

Boolean

false

Whether gRPC bean ClientInterceptor should be annotated by gRPC annotation (GrpcClientInterceptor).

This means spring-boot annotation such as @Component is invalid for gRPC bean.

Default is false.

Table 3. GrpcClientsProperties

Table 4. ClientProperties
Key Type Default Comment

inProcess

Boolean

false

useShaded

Boolean

false

target

String

localhost:6565

Address or load balance (lb:authority/host1:port1,host2:port2…​)

threadPoolBeanName

String

Thread pool bean name for gRPC executor.

initialFlowControlWindow

Int

flowControlWindow

Int

maxMessageSize

Int

maxHeaderListSize

Int

keepAliveTimeInNanos

Long

keepAliveTimeoutInNanos

Long

keepAliveWithoutCalls

Boolean

deadlineAfterInNanos

Long

loadBalancingPolicy

String

round_robin

Load balance policy: round_robin, pick_first.

Default is round_robin.

sslCertChainClassPath

String

Same classpath and file properties are alternative and classpath first

sslPrivateKeyClassPath

String

Same classpath and file properties are alternative and classpath first

sslTrustCertCollectionClassPath

String

Same classpath and file properties are alternative and classpath first

sslCertChainFile

String

Same classpath and file properties are alternative and classpath first

sslPrivateKeyFile

String

Same classpath and file properties are alternative and classpath first

sslTrustCertCollectionFile

String

Same classpath and file properties are alternative and classpath first

sslPrivateKeyPassword

String

sslClientAuth

String

Auth enum with case-ignore: none, optional or require.

Default is none.

Table 4. ClientProperties

Web

grpc-spring-boot-starter-web is used for making Controller support protobuf Message type.

By default, it uses Jackson2ObjectMapperBuilderCustomizer.