### Introduction
Spring Cloud Function allows you to invoke function via [gRPC](https://grpc.io/). While you can read more about gRPC in te provided link, this section will describe the parts relevant to Spring Cloud Function integration.
As with all other Spring-boot based frameworks all you need to do is add `spring-cloud-function-grpc` dependency to your POM.
```xml
org.springframework.cloud
spring-cloud-function-grpc
${current.version}
```
### Programming model
#### Two operation modes (client/server)
Spring Cloud Function gRPC support provides two modes of operation - _client_ and _server_. In other words when you add `spring-cloud-function-grpc` dependency to your POM you may or may not want the gRPC server as you may
only be interested in client-side utilities to invoke a function exposed via gRPC server running on some host/port.
To support these two modes Spring Cloud Function provides `spring.cloud.function.grpc.server` which defaults to `true`.
This means that the default mode of operation is _server_, since the core intention of our current gRPC support is to expose user Functions via gRPC. However, if you're only inteersted in using client-side utilities (e.g., `GrpcUtils` to help to invoke a function or convert `GrpcMessage` to Spring `Message` and vice versa), you can set this property to `false`.
In the server (default) mode, te gRPC server would be bound to te default port ***6048***. You can change it by providing
`spring.cloud.function.grpc.port` property.
#### Core Data and Service
At the center of gRPC and Spring Cloud Function integration is a canonical protobuff structure - `GrpcMessage`. It is modeled after Spring [Message](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/messaging/Message.html).
```
message GrpcSpringMessage {
bytes payload = 1;
map headers = 2;
}
```
As you can see it is a very generic structure which can support any type of data amd metadata you wish to exchange.
It also defines a `MessagingService` allowing you to generate required stubs to support true plolyglot nature of gRPC.
```
service MessagingService {
rpc biStream(stream GrpcSpringMessage) returns (stream GrpcSpringMessage);
rpc clientStream(stream GrpcSpringMessage) returns (GrpcSpringMessage);
rpc serverStream(GrpcSpringMessage) returns (stream GrpcSpringMessage);
rpc requestReply(GrpcSpringMessage) returns (GrpcSpringMessage);
}
```
That said, when using Java, you do not need to generate anything, rather identify function definition and send and receive Spring `Messages`.
You can get a pretty good idea from this [test case](https://github.com/spring-cloud/spring-cloud-function/blob/82e2583acd7c8aaaf2bc5ec935d486a336e97ae7/spring-cloud-function-grpc/src/test/java/org/springframework/cloud/function/grpc/GrpcInteractionTests.java#L49).
#### 4 Interaction RPC Modes
The gRPC provides 4 interaction modes
* Reques/Repply RPC
* Server-side streaming RPC
* Client-side streaming RPC
* Bi-directional streaming RPC
Spring Cloud Function provides support for all 4 of them.
##### Request Reply RPC
The most straight forward interaction mode is _Request/Reply_.
Suppose you have a function
```java
@EnableAutoConfiguration
public static class SampleConfiguration {
@Bean
public Function uppercase() {
return v -> v.toUpperCase();
}
}
```
After identifying this function via `spring.cloud.function.definition` property (see example [here](https://github.com/spring-cloud/spring-cloud-function/blob/ded02fec0a6d3d66b8ec00f99f28be2a4bbec668/spring-cloud-function-grpc/src/test/java/org/springframework/cloud/function/grpc/GrpcInteractionTests.java)),
you can invoke it using utility method(s) provided in `GrpcUtils` class
```java
Message message = MessageBuilder.withPayload("\"hello gRPC\"".getBytes())
.setHeader("foo", "bar")
.build();
Message reply = GrpcUtils.requestReply(message);
```
You can also provide `spring.cloud.function.definition` property via `Message` headers, to support more dynamic cases.
```java
Message message = MessageBuilder.withPayload("\"hello gRPC\"".getBytes())
.setHeader("foo", "bar")
.setHeader("spring.cloud.function.definition", "reverse")
.build();
```
##### Server-side streaming RPC
The Server-side streaming RPC allows you to reply with the stream of data.
```java
@EnableAutoConfiguration
public static class SampleConfiguration {
@Bean
public Function> stringInStreamOut() {
return value -> Flux.just(value, value.toUpperCase());
}
}
```
After identifying this function via `spring.cloud.function.definition` property (see example [here](https://github.com/spring-cloud/spring-cloud-function/blob/ded02fec0a6d3d66b8ec00f99f28be2a4bbec668/spring-cloud-function-grpc/src/test/java/org/springframework/cloud/function/grpc/GrpcInteractionTests.java)),
you can invoke it using utility method(s) provided in `GrpcUtils` class
```java
Message message = MessageBuilder.withPayload("\"hello gRPC\"".getBytes()).setHeader("foo", "bar").build();
Flux> reply =
GrpcUtils.serverStream("localhost", FunctionGrpcProperties.GRPC_PORT, message);
List> results = reply.collectList().block(Duration.ofSeconds(5));
```
You can see that gRPC stream is mapped to instance of `Flux` from [project reactor](https://projectreactor.io/)
Similarly to the _request/reply_ you can also provide `spring.cloud.function.definition` property via `Message` headers, to support more dynamic cases.
```java
Message message = MessageBuilder.withPayload("\"hello gRPC\"".getBytes())
.setHeader("foo", "bar")
.setHeader("spring.cloud.function.definition", "reverse")
.build();
```
##### Client-side streaming RPC
The Client-side streaming RPC allows you to stream input data and receive a single reply.
```java
@EnableAutoConfiguration
public static class SampleConfiguration {
@Bean
public Function, String> streamInStringOut() {
return flux -> flux.doOnNext(v -> {
try {
// do something useful
Thread.sleep(new Random().nextInt(2000)); // artificial delay
}
catch (Exception e) {
// ignore
}
}).collectList().block().toString();
}
}
```
After identifying this function via `spring.cloud.function.definition` property (see example [here](https://github.com/spring-cloud/spring-cloud-function/blob/ded02fec0a6d3d66b8ec00f99f28be2a4bbec668/spring-cloud-function-grpc/src/test/java/org/springframework/cloud/function/grpc/GrpcInteractionTests.java)),
you can invoke it using utility method(s) provided in `GrpcUtils` class
```java
List> messages = new ArrayList<>();
messages.add(MessageBuilder.withPayload("\"Ricky\"".getBytes()).setHeader("foo", "bar")
.build());
messages.add(MessageBuilder.withPayload("\"Julien\"".getBytes()).setHeader("foo", "bar")
.build());
messages.add(MessageBuilder.withPayload("\"Bubbles\"".getBytes()).setHeader("foo", "bar")
.build());
Message reply =
GrpcUtils.clientStream("localhost", FunctionGrpcProperties.GRPC_PORT, Flux.fromIterable(messages));
```
You can see that gRPC stream is mapped to instance of `Flux` from [project reactor](https://projectreactor.io/)
Unlike the _request/reply_ and _server-side streaming_, you can ONLY pass function definition via property or environment variable.
##### Bi-Directional streaming RPC
The bi-directional streaming RPC allows you to stream input and output data.
```java
@EnableAutoConfiguration
public static class SampleConfiguration {
@Bean
public Function, Flux> uppercaseReactive() {
return flux -> flux.map(v -> v.toUpperCase());
}
}
```
After identifying this function via `spring.cloud.function.definition` property (see example [here](https://github.com/spring-cloud/spring-cloud-function/blob/ded02fec0a6d3d66b8ec00f99f28be2a4bbec668/spring-cloud-function-grpc/src/test/java/org/springframework/cloud/function/grpc/GrpcInteractionTests.java)),
you can invoke it using utility method(s) provided in `GrpcUtils` class
```java
List> messages = new ArrayList<>();
messages.add(MessageBuilder.withPayload("\"Ricky\"".getBytes()).setHeader("foo", "bar")
.build());
messages.add(MessageBuilder.withPayload("\"Julien\"".getBytes()).setHeader("foo", "bar")
.build());
messages.add(MessageBuilder.withPayload("\"Bubbles\"".getBytes()).setHeader("foo", "bar")
.build());
Flux> clientResponseObserver =
GrpcUtils.biStreaming("localhost", FunctionGrpcProperties.GRPC_PORT, Flux.fromIterable(messages));
List> results = clientResponseObserver.collectList().block(Duration.ofSeconds(1));
```
You can see that gRPC stream is mapped to instance of `Flux` from [project reactor](https://projectreactor.io/)
Unlike the _request/reply_ and _server-side streaming_, you can ONLY pass function definition via property or environment variable.
#### Pluggable protobuf extension
While the core data object and its corresponding schema <> are modeled after Spring Message and can represent
virtually any object, there are times when you may want to plug-in your own protobuf services.
Spring Cloud Function provides such support by allowing you to develop extensions, which once exist could be enabled by simply
including its dependency in the POM. Such extensions are just another spring-boot project that has dependency on `spring-cloud-function-grpc`
```xml
org.springframework.cloud
spring-cloud-function-grpc
```
It must also contain 3 classes; 1) Its configuration class, 2) Type converter for the actual protobuf 'message'and 3) Service handler
where you would normally implement your handling functionality. However instead of implementing full functionality you can model your service
after MessagingService provided by us and if you do you can rely on the existing implementation of the core interaction models provided by gRPC
In fact Spring Cloud Function provides one of such extensions to support [Cloud Events](https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext) proto, so you can model yours after it.
#### Multiple services on classpath
With the protobuf extension mentioned in the previous section you may very well end up with several services on the classpath.
By default each available service will be enabled. However, if your intention is to only use one, you can specify which one by providing
its class name via `spring.cloud.function.grpc.service-class-name` property:
```
--spring.cloud.function.grpc.service-class-name=org.springframework.cloud.function.grpc.ce.CloudEventHandler
```