Search Tutorials


Spring Boot + gRPC Deadline Example | JavaInUse

Spring Boot + gRPC Deadline Example

Consider a scenario where we have implemented a gRPC server and gRPC client. Due to some reason the gRPC server has become very slow and is taking a lot of time to process the request. Or in some cases the gRPC server is not returning a response at all. In such a scenario how will the gRPC client behave?
According to the gRPC documentation the gRPC client has no default timeout. So the gRPC client will keep on waiting indefinitely. This is not the behaviour that we want. We want our solutions to be fail fast.
gRPC Deadline Example
In this tutorial we will be implementing gRPC deadline for the gRPC client using which we specify that the gRPC client should only wait for a limited amount of time else it should throw an exception. This helps prevent the client from waiting forever if the server is taking too long to respond.
Using a gRPC deadline can be useful in scenarios where timely responses are critical. For example, in a real-time messaging application, setting a deadline ensures that users receive responses quickly without experiencing delays. It also helps manage network resources efficiently by preventing connections from being tied up indefinitely waiting for a response. Ultimately, gRPC deadlines help improve the overall performance and responsiveness of client-server communication.
Spring Boot + gRPC Deadline Example

Video

This tutorial is explained in the below Youtube Video.

gRPC - Table of Contents

Spring Boot+ gRPC Hello World Example Spring Boot gRPC Server + C# gRPC Client Example Spring Boot 3 + gRPC - Types of gRPC Spring Boot 3 + gRPC Unary Example Spring Boot 3 + gRPC Server Streaming Example Spring Boot 3 + gRPC Client Streaming Example Spring Boot 3 + gRPC Bidirectional Streaming Example Spring Boot + gRPC Deadline Example Spring Boot + gRPC Error Handling Example Spring Boot + gRPC Error Handling - Using Metadata Trailer Spring Boot + gRPC Error Handling - Global Exception Handler Using GrpcAdvice

Implementation

We will be modifying the code we had implemented for Spring Boot gRPC + Server Streaming Example.
For Server Streaming we had implemented the following code where there is a delay of 5 secs before sending the streaming data.
package com.javainuse.bank.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

import com.javainuse.bank.model.Transaction;
import com.javainuse.banking.AccountRequest;
import com.javainuse.banking.TransactionDetail;
import com.javainuse.banking.TransactionDetailList;
import com.javainuse.banking.TransactionServiceGrpc;

import io.grpc.Metadata;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;

@GrpcService
public class TransactionHistoryService extends TransactionServiceGrpc.TransactionServiceImplBase {

	@Override
	public void streamTransactions(AccountRequest request, StreamObserver<TransactionDetailList> responseObserver) {

		// Assuming you have a method to fetch transaction details based on the duration
		// in days
		List<Transaction> transactions = fetchTransactions(request.getDurationInDays());
		int batchSize = 3; // How many transactions to send at once

		for (int i = 0; i < transactions.size(); i += batchSize) {
			int endIndex = Math.min(i + batchSize, transactions.size());
			List<Transaction> batchTransactions = transactions.subList(i, endIndex);

			TransactionDetailList.Builder transactionDetailListBuilder = TransactionDetailList.newBuilder();

			for (Transaction transaction : batchTransactions) {
				TransactionDetail transactionDetail = createTransactionDetailFromTransaction(transaction);
				transactionDetailListBuilder.addTransactionDetails(transactionDetail);
			}
			TransactionDetailList transactionDetailList = transactionDetailListBuilder.build();

			responseObserver.onNext(transactionDetailList);

			// Delay between sending batches (if necessary)
			// You can adjust this based on your requirements
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				e.printStackTrace();
				responseObserver.onError(e);
				return;
			}<
		}

		// Send completion of response with final metadata

		responseObserver.onCompleted();
	}

	private TransactionDetail createTransactionDetailFromTransaction(Transaction transaction) {
		return TransactionDetail.newBuilder().setTransactionId(transaction.getId())
				.setTransactionType(transaction.getType()).setTransactionAmount(transaction.getAmount()).build();
	}

	private List<Transaction> fetchTransactions(int durationInDays) {
		List<Transaction> transactions = new ArrayList<>();

		// Mock data for transaction details
		Transaction transaction1 = new Transaction("1", "Deposit", 100.0f);
		Transaction transaction2 = new Transaction("2", "Withdrawal", 50.0f);
		Transaction transaction3 = new Transaction("3", "Transfer", 75.0f);
		Transaction transaction4 = new Transaction("4", "Deposit", 200.0f);
		Transaction transaction5 = new Transaction("5", "Withdrawal", 30.0f);

		transactions.addAll(Arrays.asList(transaction1, transaction2, transaction3, transaction4, transaction5));

		return transactions;

	}
}
The client code makes use of the async stub to call the server stream gRPC server. When we run the client code we get the following output after 5 seconds.
Bank Server Streaming gRPC output
Let us now implement the gRPC client deadline which sepcifies a timeout value of 2 seconds. So if the client does not get a response till 2 seconds it will throw a DEADLINE_EXCEEDED exception.
package com.javainuse.bank.service;

import java.util.concurrent.TimeUnit;

import com.javainuse.banking.AccountRequest;
import com.javainuse.banking.TransactionDetailList;
import com.javainuse.banking.TransactionServiceGrpc;

import io.grpc.Deadline;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.StreamObserver;

public class TransactionServiceClient {

	private final ManagedChannel channel;
	private final TransactionServiceGrpc.TransactionServiceStub asyncStub;

	public TransactionServiceClient(String host, int port) {
		this(ManagedChannelBuilder.forAddress(host, port).usePlaintext().build());
	}

	public TransactionServiceClient(ManagedChannel channel) {
		this.channel = channel;
		asyncStub = TransactionServiceGrpc.newStub(channel);
	}

	public void streamTransactions(String accountNumber, int durationInDays) {
		AccountRequest request = AccountRequest.newBuilder().setAccountNumber(accountNumber)
				.setDurationInDays(durationInDays).build();

		asyncStub.withDeadline(Deadline.after(2, TimeUnit.SECONDS)).streamTransactions(request,
				new StreamObserver<TransactionDetailList>() {
					@Override
					public void onNext(TransactionDetailList transactionDetail) {
						// Handle each incoming TransactionDetail here
						System.out.println("Received transaction detail: " + transactionDetail);
					}

					@Override
					public void onError(Throwable throwable) {
						System.err.println("Error occurred during transaction streaming: " + throwable);
					}

					@Override
					public void onCompleted() {
						System.out.println("Transaction streaming completed");
					}
				});
	}

	public void shutdown() throws InterruptedException {
		channel.shutdown().awaitTermination(3, TimeUnit.SECONDS);
	}

}
If we run the client we now get the io.grpc.StatusRuntimeException: DEADLINE_EXCEEDED exception.
io.grpc.StatusRuntimeException: DEADLINE_EXCEEDED

Download Source Code

Download it - Spring Boot + gRPC Deadline Client Example
Download it - Spring Boot + gRPC Deadline Server Example