Contents
- What is gRPC?
- What you’ll learn
- Getting started
- Creating and defining the gRPC server service
- Implementing the unary call
- Implementing the server streaming call
- Implementing the client streaming call
- Implementing the bidirectional streaming call
- Testing the application
- Great work! You’re done!
- Guide Attribution
Tags
Streaming messages between client and server services using gRPC
Prerequisites:
Learn how to use gRPC unary calls, server streaming, client streaming, and bidirectional streaming to communicate between Java client and server services with Open Liberty.
What is gRPC?
The gRPC Remote Procedure Call is a technology that implements remote procedure call (RPC) style APIs with HTTP/2. Typically, gRPC uses protocol buffers to define the format of data to be transferred and the service interfaces to access it, which include service calls and expected messages. For each service defined in a .proto
file, gRPC uses the definition to generate the skeleton code for users to implement and extend. Protocol buffers use a binary format to send and receive messages that is faster and more lightweight than the JSON that is typically used in RESTful APIs.
Protocol buffers allow cross-project support through the .proto
file. As a result, gRPC clients and servers can run and communicate with each other from different environments. For example, a gRPC client running on a Java virtual machine can call a gRPC server developed in any other supported language. This feature of protocol buffers allows for easier integration between services.
What you’ll learn
You will learn how to create gRPC services and their clients by using protocol buffers and how to implement them with Open Liberty. You will use Maven to generate the gRPC stubs, deploy the services, and to interact with the running Liberty runtime.
The application that you will build in this guide consists of three projects: the systemproto
model project, the query
client service, and the system
server service.
The query
service implements four RESTful APIs by using four different gRPC streaming methods.
-
Unary RPC: The client sends a single request and receives a single response.
-
Server streaming RPC: The client sends a single request and the server returns a stream of messages.
-
Client streaming RPC: The client sends a stream of messages and the server responds with a single message.
-
Bidirectional RPC: Both client and server send a stream of messages. The client and server can read and write messages in any order.
Getting started
The fastest way to work through this guide is to clone the Git repository and use the projects that are provided inside:
git clone https://github.com/openliberty/guide-grpc-intro.git
cd guide-grpc-intro
The start
directory contains the starting project that you will build upon.
The finish
directory contains the finished project that you will build.
Before you begin, make sure you have all the necessary prerequisites.
Try what you’ll build
The finish
directory in the root of this guide contains the finished application. Give it a try before you proceed.
To try out the application, first go to the finish
directory and run the following Maven goal to generate all the gRPC abstract classes defined in the .proto
file.
cd finish mvn -pl systemproto install
Start the system
service by running the following command:
mvn -pl system liberty:run
Next, open another command-line session, navigate to the finish
directory, and start the query
service by using the following command:
mvn -pl query liberty:run
Wait until you see the The defaultServer server is ready to run a smarter planet
message in your consoles. Then, point your browser to the http://localhost:9081/query/properties/os.name URL to test out the basic unary service. You will see your operating system name.
Next, point your browser to the following URLs to try out the corresponding streaming RPC call:
-
Visit http://localhost:9081/query/properties/os to test out server streaming call. The details of your localhost operating system are displayed.
-
Visit http://localhost:9081/query/properties/user to test out client streaming call. The details of your localhost user properties are displayed.
-
Visit http://localhost:9081/query/properties/java to test out bidirectional streaming call. The details of your localhost Java properties are displayed.
Observe the output from the consoles running the system
and query
services.
After you are finished checking out the application, stop both the query
and system
services by pressing CTRL+C
in the command-line sessions where you ran them. Alternatively, you can run the following goals from the finish
directory in another command-line session:
mvn -pl system liberty:stop mvn -pl query liberty:stop
Creating and defining the gRPC server service
Navigate to the start
directory to begin.
First, create the .proto
file and generate gRPC classes. You will implement the gRPC server service with the generated classes later. The .proto
file defines all the service calls and message types. The message types are used in the service call definition for the parameters and returns.
Create theSystemService.proto
file.systemproto/src/main/proto/SystemService.proto
SystemService.proto
pom.xml
1<?xml version="1.0" encoding="UTF-8"?>
2<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
3 <modelVersion>4.0.0</modelVersion>
4 <groupId>io.openliberty.guides</groupId>
5 <artifactId>systemproto</artifactId>
6 <version>1.0-SNAPSHOT</version>
7 <packaging>jar</packaging>
8
9 <properties>
10 <maven.compiler.source>11</maven.compiler.source>
11 <maven.compiler.target>11</maven.compiler.target>
12 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
13 </properties>
14
15 <dependencies>
16 <!-- Provided dependencies -->
17 <dependency>
18 <groupId>jakarta.platform</groupId>
19 <artifactId>jakarta.jakartaee-api</artifactId>
20 <version>10.0.0</version>
21 <scope>provided</scope>
22 </dependency>
23 <!-- grpc compile dependencies -->
24 <!-- tag::grpc[] -->
25 <dependency>
26 <groupId>io.grpc</groupId>
27 <artifactId>grpc-protobuf</artifactId>
28 <version>1.68.1</version>
29 <scope>provided</scope>
30 </dependency>
31 <!-- end::grpc[] -->
32 <!-- tag::grpc-stub[] -->
33 <dependency>
34 <groupId>io.grpc</groupId>
35 <artifactId>grpc-stub</artifactId>
36 <version>1.68.1</version>
37 <scope>provided</scope>
38 </dependency>
39 <!-- end::grpc-stub[] -->
40 <!-- tag::javax[] -->
41 <dependency>
42 <groupId>javax.annotation</groupId>
43 <artifactId>javax.annotation-api</artifactId>
44 <version>1.3.2</version>
45 <scope>provided</scope>
46 </dependency>
47 <!-- end::javax[] -->
48 </dependencies>
49
50 <build>
51 <finalName>${project.artifactId}</finalName>
52 <!-- tag::osmavenplugin[] -->
53 <extensions>
54 <extension>
55 <groupId>kr.motd.maven</groupId>
56 <artifactId>os-maven-plugin</artifactId>
57 <version>1.7.1</version>
58 </extension>
59 </extensions>
60 <!-- end::osmavenplugin[] -->
61 <plugins>
62 <plugin>
63 <groupId>org.apache.maven.plugins</groupId>
64 <artifactId>maven-compiler-plugin</artifactId>
65 <version>3.13.0</version>
66 </plugin>
67 <!-- tag::protobufmavenplugin[] -->
68 <plugin>
69 <groupId>org.xolstice.maven.plugins</groupId>
70 <artifactId>protobuf-maven-plugin</artifactId>
71 <version>0.6.1</version>
72 <configuration>
73 <protocArtifact>com.google.protobuf:protoc:3.21.9:exe:${os.detected.classifier}</protocArtifact>
74 <pluginId>grpc-java</pluginId>
75 <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.51.0:exe:${os.detected.classifier}</pluginArtifact>
76 </configuration>
77 <executions>
78 <execution>
79 <goals>
80 <goal>compile</goal>
81 <goal>compile-custom</goal>
82 </goals>
83 </execution>
84 </executions>
85 </plugin>
86 <!-- end::protobufmavenplugin[] -->
87 </plugins>
88 </build>
89</project>
The first few lines define the syntax
, package
, and option
basic configuration of the .proto
file. The SystemService
service contains the four service calls that you will implement in the coming sections.
The getProperty
RPC defines the unary call. In this call, the client service sends a SystemPropertyName
message to the server service, which returns a SystemPropertyValue
message with the property value. The SystemPropertyName
and SystemPropertyValue
message types define that the propertyName
and propertyValue
fields must be string.
The getServerStreamingProperties
RPC defines the server streaming call. The client service sends a SystemPropertyPrefix
message to the server service. The server service returns a stream of SystemProperty
messages. Each SystemProperty
message contains propertyName
and propertyValue
strings.
The getClientStreamingProperties
RPC defines the client streaming call. The client service streams SystemPropertyName
messages to the server service. The server service returns a SystemProperties
message that contains a map of the properties with their respective values.
The getBidirectionalProperties
RPC defines the bidirectional streaming call. In this service, the client service streams SystemPropertyName
messages to the server service. The server service returns a stream of SystemProperty
messages.
To compile the .proto
file, the pom.xml
Maven configuration file needs the grpc-protobuf
, grpc-stub
, javax.annotation-api
dependencies, and the protobuf-maven-plugin
plugin. To install the correct version of the Protobuf compiler automatically, the os-maven-plugin
extension is required in the build
configuration.
Run the following command to generate the gRPC classes.
mvn -pl systemproto install
Implementing the unary call
Navigate to the start
directory.
When you run Open Liberty in dev mode, dev mode listens for file changes and automatically recompiles and deploys your updates whenever you save a new change. Run the following command to start the system
service in dev mode:
mvn -pl system liberty:dev
Open another command-line session, navigate to the start
directory, and run the following command to start the query
service in dev mode:
mvn -pl query liberty:dev
After you see the following message, your Liberty instances are ready in dev mode:
************************************************************** * Liberty is running in dev mode.
Dev mode holds your command-line session to listen for file changes. Open another command-line session and navigate to the start
directory to continue, or open the project in your editor.
Start by implementing the first service call, the unary call. In this service call, the query
client service sends a property to the system
server service, which returns the property value. This type of service call resembles a RESTful API.
Create theSystemService
class.system/src/main/java/io/openliberty/guides/system/SystemService.java
SystemService.java
The SystemService
class extends the SystemServiceGrpc
class that is generated by the .proto
file. The four types of services defined in the proto file are implemented in this class.
The getProperty()
method implements the unary RPC call defined in the .proto
file. The getPropertyName()
getter method that is generated by gRPC retrieves the property name from the client, and stores it into the pName
variable. The System property value is stored into the pValue
variable. The gRPC library will create a SystemPropertyValue
message, with its type defined in the SystemService.proto
file. Then, the message is sent to the client service through the StreamObserver
by using its onNext()
and onComplete()
methods.
Replace the system'sserver.xml
configuration file.system/src/main/liberty/config/server.xml
system/server.xml
Add the grpc
feature to the Liberty server.xml
configuration file. This feature enables applications running on Liberty to provide gRPC services. Configure the grpc
element with the maxInboundMessageSize
attribute to restrict inbound messages to 1024 bytes. This configuration applies universally to all gRPC services running on the server, as indicated by the wildcard (*
) in the target
attribute. If you want to learn more about configuration for the grpc
element, see the GRPC Server Properties.
Next, implement the corresponding REST endpoint in the query
service.
Create thePropertiesResource
class.query/src/main/java/io/openliberty/guides/query/PropertiesResource.java
PropertiesResource.java
The PropertiesResource
class provides RESTful endpoints to interact with the system
service. The /query/properties/${property}
endpoint uses the unary service call to get the property value from the system
service. The endpoint creates a channel
, which it uses to create a client by the SystemServiceGrpc.newBlockingStub()
API. The endpoint then uses the client to get the property value, shuts down the channel, and immediately returns the value from the system
service response.
Replace the query'sserver.xml
configuration file.query/src/main/liberty/config/server.xml
query/server.xml
Add the grpcClient
feature to the Liberty server.xml
configuration file for the query
service. This feature enables gRPC client support on Liberty. Configure the grpcClient
element with the headersToPropagate
attribute to propagate cookies. This configuration applies universally to all gRPC client calls, as indicated by the wildcard (*
) in the host
attribute. If you want to learn more about grpcClient
element configuration, see the GRPC Client Properties.
Because you are running the system
and query
services in dev mode, the changes that you made are automatically picked up. You’re now ready to check out your application in your browser.
Point your browser to the http://localhost:9081/query/properties/os.name URL to test out the unary service call. Your operating system name is displayed.
Implementing the server streaming call
In the server streaming call, the query
client service provides the /query/properties/os
endpoint that sends a message to the system
server service. The system
service streams any properties that start with os.
back to the query
service. A channel is created between the query
and the system
services to stream messages. The channel is closed by the system
service only after sending the last message to the query
service.
Update the SystemService
class to implement the server streaming RPC call.
Replace theSystemService
class.system/src/main/java/io/openliberty/guides/system/SystemService.java
SystemService.java
The getServerStreamingProperties()
method implements the server streaming RPC call. The getPropertyPrefix()
getter method retrieves the property prefix from the client. Properties that start with the prefix
are filtered out. For each property, a SystemProperty
message is built and streamed to the client through the StreamObserver
by using its onNext()
method. When all properties are streamed, the service stops streaming by calling the onComplete()
method.
Update the PropertiesResource
class to implement the /query/properties/os
endpoint of the query
service.
Replace thePropertiesResource
class.query/src/main/java/io/openliberty/guides/query/PropertiesResource.java
PropertiesResource.java
The endpoint creates a channel
to the system
service and a client
by using the SystemServiceGrpc.newStub()
API. Then, it calls the getServerStreamingProperties()
method with an implementation of the StreamObserver
interface. The onNext()
method receives messages streaming from the server service individually and stores them into the properties
placeholder. After all properties are received, the system
service shuts down the channel
and returns the placeholder. Because the RPC call is asynchronous, a CountDownLatch
instance synchronizes the streaming flow.
Point your browser to the http://localhost:9081/query/properties/os URL to test out the server streaming call. The os.
properties from the system
service are displayed. Observe the output from the consoles running the system
and query
services.
Implementing the client streaming call
In the client streaming call, the query
client service provides the /query/properties/user
endpoint, which streams the user properties to the system
server service. The system
service returns a map of user properties with their values.
Update the SystemService
class to implement the client streaming RPC call.
Replace theSystemService
class.system/src/main/java/io/openliberty/guides/system/SystemService.java
SystemService.java
The getClientStreamingProperties()
method implements client streaming RPC call. This method returns an instance of the StreamObserver
interface. Its onNext()
method receives the messages from the client individually and stores the property values into the properties
map placeholder. When the streaming is completed, the properties
placeholder is sent back to the client by the onCompleted()
method.
Update the PropertiesResource
class to implement the /query/properties/user
endpoint of the query service.
Replace thePropertiesResource
class.query/src/main/java/io/openliberty/guides/query/PropertiesResource.java
PropertiesResource.java
After a connection is created between the two services, the client.getClientStreamingProperties()
method is called to get a stream
and collect the properties with property names that are prefixed by user.
. The method creates a SystemPropertyName
message individually and sends the message to the server by the stream::onNext
action. When all property names are sent, the onCompleted()
method is called to finish the streaming. Again, a CountDownLatch
instance synchronizes the streaming flow.
Point your browser to the http://localhost:9081/query/properties/user URL to test the client streaming call. The user.
properties from the system
service are displayed. Observe the output from the consoles running the system
and query
services.
Implementing the bidirectional streaming call
In the bidirectional streaming call, the query
client service provides the /query/properties/java
endpoint, which streams the property names that start with java.
to the system
server service. The system
service streams the property values back to the query
service.
Update the SystemService
class to implement the bidirectional streaming RPC call.
Replace theSystemService
class.system/src/main/java/io/openliberty/guides/system/SystemService.java
SystemService.java
The getBidirectionalProperties()
method implements bidirectional streaming RPC call. This method returns an instance of the StreamObserver
interface. Its onNext()
method receives the messages from the client individually, creates a SystemProperty
message with the property name and value, and sends the message back to the client. When the client streaming is completed, the method closes the server streaming by calling the onCompleted()
method.
Update the PropertiesResource
class to implement of /query/properties/java
endpoint of the query service.
Replace thePropertiesResource
class.query/src/main/java/io/openliberty/guides/query/PropertiesResource.java
PropertiesResource.java
After a connection is created between the two services, the client.getBidirectionalProperties()
method is called with an implementation of the StreamObserver
interface. The onNext()
method receives messages that are streaming from the server individually and stores them into the properties
placeholder. Then, collect the properties . For each property name that starts with java.
, a SystemPropertyName
message is created and sent to the server by the stream::onNext
action. When all property names are sent, the streaming is ended by calling the onCompleted()
method. Again, a CountDownLatch
instance synchronizes the streaming flow.
Point your browser to the http://localhost:9081/query/properties/java URL to test the bidirectional streaming call. The java.
properties from the system
service are displayed. Observe the output from the consoles running the system
and query
services.
Testing the application
Although you can test your application manually, automated tests ensure consistent code quality by triggering a failure whenever a code change introduces a defect. In this section, you’ll create unit tests for the gRPC server service and integration tests for the query
service.
Implementing unit tests for the gRPC server service
system/pom.xml
The pom.xml
Maven configuration file already specifies the required dependencies, including JUnit5
, grpc-testing
, and mockito-core
libraries. The grpc-testing
dependency provides utilities for testing gRPC services and creates a mock gRPC server that simulates client-server communication during testing. The mockito-core
dependency enables the Mockito mocking framework.
Create theSystemServiceTest
class.system/src/test/java/io/openliberty/guides/system/SystemServiceTest.java
SystemServiceTest.java
SystemService.java
In the setUp()
static method, create and start the inProcessServer
in-process gRPC server. Then, create the inProcessChannel
in-process channel that connects to the inProcessServer
server running in the same JVM process. The unit tests can make calls to the gRPC server by using the same method signatures and functionalities as the gRPC client, even though they use different blockingStub
or asyncStub
stubs through the same channel.
In the tearDown()
static method, shut down the inProcessChannel
in-process channel and the inProcessServer
in-process gRPC server.
The testGetProperty()
tests the unary call to retrieve a single system property value.
The testGetServerStreamingProperties()
tests the server streaming call to retrieve multiple system property values with a given property prefix.
The testGetClientStreamingProperties()
tests the client streaming call to retrieve multiple system property values with given property names.
The testGetBidirectionalProperties()
tests the bidirectional streaming call to retrieve multiple system property values with given property names.
Running unit tests for the gRPC server service
Because you started Open Liberty in dev mode, you can run the tests by pressing the enter/return
key from the command-line session where you started the system
service.
If the tests pass, you see output similar to the following example:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running io.openliberty.guides.system.SystemServiceTest
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.527 s - in io.openliberty.guides.system.SystemServiceTest
Results:
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
Implementing integration tests for the query service
In this section, you’ll write integration tests using Jakarta Restful Web Services Client APIs to test the query
service.
Create theQueryIT
class.query/src/test/java/it/io/openliberty/guides/query/QueryIT.java
QueryIT.java
The testGetPropertiesString()
tests the /query/properties/os.name
endpoint and confirms that a response is received.
The testGetOSProperties()
tests the /query/properties/os
endpoint and confirms that a response is received.
The testGetUserProperties()
tests the /query/properties/user
endpoint and confirms that a response is received.
The testGetJavaProperties()
tests the /query/properties/java
endpoint and confirms that a response is received.
Running integration tests for the query service
Because you started Open Liberty in dev mode, you can run the tests by pressing the enter/return
key from the command-line session where you started the query
service.
If the tests pass, you see output similar to the following example:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running it.io.openliberty.guides.query.QueryIT
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.247 s - in it.io.openliberty.guides.query.QueryIT
Results:
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
When you are done checking out the services, exit dev mode by pressing CTRL+C
in the command-line sessions where you ran the system
and query
services.
Great work! You’re done!
You just developed a Java application that implements four types of gRPC calls with Open Liberty. For more information, see Provide and consume gRPC services on Open Liberty in the Open Liberty docs.
Guide Attribution
Streaming messages between client and server services using gRPC by Open Liberty is licensed under CC BY-ND 4.0
Prerequisites:
Nice work! Where to next?
What did you think of this guide?
Thank you for your feedback!
What could make this guide better?
Raise an issue to share feedback
Create a pull request to contribute to this guide
Need help?
Ask a question on Stack Overflow