Skip to content

OpenLiberty/guide-grpc-intro

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Streaming messages between client and server services using gRPC

Note
This repository contains the guide documentation source. To view the guide in published form, view it on the Open Liberty website.

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.

Application architecture of the gRPC application covered in guide

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:

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 the SystemService.proto file.
systemproto/src/main/proto/SystemService.proto

SystemService.proto

link:finish/systemproto/src/main/proto/SystemService.proto[role=include]

pom.xml

link:finish/systemproto/pom.xml[role=include]

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 the SystemService class.
system/src/main/java/io/openliberty/guides/system/SystemService.java

SystemService.java

link:finish/system/src/main/java/io/openliberty/guides/system/SystemService.java[role=include]

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's server.xml configuration file.
system/src/main/liberty/config/server.xml

system/server.xml

link:finish/system/src/main/liberty/config/server.xml[role=include]

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 the PropertiesResource class.
query/src/main/java/io/openliberty/guides/query/PropertiesResource.java

PropertiesResource.java

link:finish/query/src/main/java/io/openliberty/guides/query/PropertiesResource.java[role=include]

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's server.xml configuration file.
query/src/main/liberty/config/server.xml

query/server.xml

link:finish/query/src/main/liberty/config/server.xml[role=include]

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 the SystemService class.
system/src/main/java/io/openliberty/guides/system/SystemService.java

SystemService.java

link:finish/system/src/main/java/io/openliberty/guides/system/SystemService.java[role=include]

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 the PropertiesResource class.
query/src/main/java/io/openliberty/guides/query/PropertiesResource.java

PropertiesResource.java

link:finish/query/src/main/java/io/openliberty/guides/query/PropertiesResource.java[role=include]

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 the SystemService class.
system/src/main/java/io/openliberty/guides/system/SystemService.java

SystemService.java

link:finish/system/src/main/java/io/openliberty/guides/system/SystemService.java[role=include]

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 the PropertiesResource class.
query/src/main/java/io/openliberty/guides/query/PropertiesResource.java

PropertiesResource.java

link:finish/query/src/main/java/io/openliberty/guides/query/PropertiesResource.java[role=include]

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 the SystemService class.
system/src/main/java/io/openliberty/guides/system/SystemService.java

SystemService.java

link:finish/system/src/main/java/io/openliberty/guides/system/SystemService.java[role=include]

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 the PropertiesResource class.
query/src/main/java/io/openliberty/guides/query/PropertiesResource.java

PropertiesResource.java

link:finish/query/src/main/java/io/openliberty/guides/query/PropertiesResource.java[role=include]

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

link:finish/system/pom.xml[role=include]

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 the SystemServiceTest class.
system/src/test/java/io/openliberty/guides/system/SystemServiceTest.java

SystemServiceTest.java

link:finish/system/src/test/java/io/openliberty/guides/system/SystemServiceTest.java[role=include]

SystemService.java

link:finish/system/src/main/java/io/openliberty/guides/system/SystemService.java[role=include]

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 the QueryIT class.
query/src/test/java/it/io/openliberty/guides/query/QueryIT.java

QueryIT.java

link:finish/query/src/test/java/it/io/openliberty/guides/query/QueryIT.java[role=include]

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.

About

A guide on how to use gRPC unary calls, server streaming, client streaming, and bidirectional streaming to communicate between Java client and server services with Open Liberty.

Resources

License

Stars

Watchers

Forks

Packages

No packages published