gRPC Java для Java: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

gRPC Java для Java: полное руководство с примерами

Добро пожаловать в подробное руководство по библиотеке gRPC Java — высокопроизводительной среде удаленного вызова процедур (RPC) от Google. Эта статья предназначена для разработчиков Java, которые хотят освоить современный способ создания микросервисов и клиент-серверных приложений с использованием Protocol Buffers (protobuf) в качестве языка описания интерфейсов и сериализации данных.

1. Что это и зачем нужна

gRPC — это современная, высокопроизводительная среда RPC (Remote Procedure Call), разработанная Google. Она использует HTTP/2 для транспорта, Protocol Buffers (protobuf) для сериализации данных и предоставляет множество функций, таких как двунаправленная потоковая передача, аутентификация и балансировка нагрузки. Библиотека gRPC Java является официальной реализацией gRPC для языка Java.

Зачем использовать gRPC Java? В отличие от традиционных REST API, которые используют JSON и HTTP/1.1, gRPC предлагает ряд преимуществ:

  • Высокая производительность: Использование HTTP/2 позволяет мультиплексировать запросы, а protobuf обеспечивает компактную и быструю сериализацию (в 3-10 раз быстрее JSON).
  • Строгая типизация: Контракты API определяются в .proto файлах, что позволяет генерировать строго типизированный код как для сервера, так и для клиента.
  • Поддержка потоковой передачи: gRPC поддерживает четыре типа RPC: унарный (один запрос — один ответ), серверный стриминг, клиентский стриминг и двунаправленный стриминг.
  • Встроенная поддержка аутентификации и TLS: gRPC легко интегрируется с SSL/TLS и различными механизмами аутентификации.

Эта библиотека идеально подходит для создания микросервисных архитектур, систем реального времени (чат, игры), мобильных бэкендов и любых других приложений, где важна скорость и надежность.

2. Установка

Для установки gRPC Java в ваш проект Maven или Gradel необходимо добавить соответствующие зависимости. Ниже приведены точные команды для Maven (в файл pom.xml) и Gradle (в файл build.gradle).

Maven (pom.xml)

<!-- В секцию <dependencies> -->
<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-netty-shaded</artifactId>
    <version>1.62.2</version>
</dependency>
<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-protobuf</artifactId>
    <version>1.62.2</version>
</dependency>
<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-stub</artifactId>
    <version>1.62.2</version>
</dependency>
<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-api</artifactId>
    <version>1.62.2</version>
</dependency>
<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
</dependency>

<!-- Плагин для компиляции .proto файлов -->
<build>
    <extensions>
        <extension>
            <groupId>kr.motd.maven</groupId>
            <artifactId>os-maven-plugin</artifactId>
            <version>1.7.1</version>
        </extension>
    </extensions>
    <plugins>
        <plugin>
            <groupId>org.xolstice.maven.plugins</groupId>
            <artifactId>protobuf-maven-plugin</artifactId>
            <version>0.6.1</version>
            <configuration>
                <protocArtifact>com.google.protobuf:protoc:3.25.3:exe:${os.detected.classifier}</protocArtifact>
                <pluginId>grpc-java</pluginId>
                <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.62.2:exe:${os.detected.classifier}</pluginArtifact>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                        <goal>compile-custom</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Gradle (build.gradle)

plugins {
    id 'java'
    id 'com.google.protobuf' version '0.9.4'
}

group 'com.example'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'io.grpc:grpc-netty-shaded:1.62.2'
    implementation 'io.grpc:grpc-protobuf:1.62.2'
    implementation 'io.grpc:grpc-stub:1.62.2'
    implementation 'io.grpc:grpc-api:1.62.2'
    implementation 'javax.annotation:javax.annotation-api:1.3.2'
}

protobuf {
    protoc {
        artifact = "com.google.protobuf:protoc:3.25.3"
    }
    plugins {
        grpc {
            artifact = 'io.grpc:protoc-gen-grpc-java:1.62.2'
        }
    }
    generateProtoTasks {
        all()*.plugins {
            grpc {}
        }
    }
}

После настройки выполните команду mvn clean compile (или gradle build), чтобы сгенерировать классы из .proto файлов.

3. Быстрый старт — минимальный рабочий пример

Создадим простое приложение "Hello World" с использованием gRPC. Сначала определим сервис в файле hello.proto:

syntax = "proto3";

package com.example.grpc;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

После генерации кода (с помощью плагина Maven/Gradle) создадим сервер:

package com.example.grpc;

import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.stub.StreamObserver;

import java.io.IOException;

public class HelloWorldServer {
    public static void main(String[] args) throws IOException, InterruptedException {
        Server server = ServerBuilder.forPort(50051)
                .addService(new GreeterImpl())
                .build()
                .start();
        System.out.println("Сервер запущен на порту 50051");
        server.awaitTermination();
    }

    static class GreeterImpl extends GreeterGrpc.GreeterImplBase {
        @Override
        public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
            HelloReply reply = HelloReply.newBuilder()
                    .setMessage("Привет, " + req.getName() + "!")
                    .build();
            responseObserver.onNext(reply);
            responseObserver.onCompleted();
        }
    }
}

И клиент:

package com.example.grpc;

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;

public class HelloWorldClient {
    public static void main(String[] args) {
        ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50051)
                .usePlaintext()
                .build();

        GreeterGrpc.GreeterBlockingStub stub = GreeterGrpc.newBlockingStub(channel);

        HelloRequest request = HelloRequest.newBuilder()
                .setName("Мир")
                .build();

        HelloReply response = stub.sayHello(request);
        System.out.println("Ответ от сервера: " + response.getMessage());

        channel.shutdown();
    }
}

Запустите сервер, затем клиент — вы увидите сообщение "Привет, Мир!".

4. Основные методы/функции/классы

4.1. ServerBuilder.forPort(int port)

Сигнатура: public static ServerBuilder<?> forPort(int port)

Что делает: Создает новый экземпляр ServerBuilder, который будет слушать указанный порт. Это отправная точка для создания gRPC сервера.

Server server = ServerBuilder.forPort(8080)
        .addService(new MyServiceImpl())
        .build()
        .start();

4.2. ServerBuilder.addService(BindableService service)

Сигнатура: public ServerBuilder<T> addService(BindableService service)

Что делает: Добавляет реализацию сервиса (класс, наследующий от сгенерированного ImplBase) на сервер. Можно добавить несколько сервисов.

Server server = ServerBuilder.forPort(50051)
        .addService(new GreeterImpl())
        .addService(new CalculatorImpl())
        .build();

4.3. ManagedChannelBuilder.forAddress(String host, int port)

Сигнатура: public static ManagedChannelBuilder<?> forAddress(String host, int port)

Что делает: Создает строитель для управляемого канала связи с удаленным сервером. Используется для создания клиентского подключения.

ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50051)
        .usePlaintext()
        .build();

4.4. GreeterGrpc.newBlockingStub(Channel channel)

Сигнатура: public static GreeterBlockingStub newBlockingStub(io.grpc.Channel channel)

Что делает: Создает синхронный (блокирующий) заглушку для вызова методов сервиса. Каждый вызов блокирует поток до получения ответа.

GreeterGrpc.GreeterBlockingStub blockingStub = GreeterGrpc.newBlockingStub(channel);
HelloReply response = blockingStub.sayHello(request);

4.5. GreeterGrpc.newStub(Channel channel)

Сигнатура: public static GreeterStub newStub(io.grpc.Channel channel)

Что делает: Создает асинхронную заглушку. Вызовы не блокируют поток, а результат возвращается через колбэк StreamObserver.

GreeterGrpc.GreeterStub asyncStub = GreeterGrpc.newStub(channel);
asyncStub.sayHello(request, new StreamObserver<HelloReply>() {
    @Override
    public void onNext(HelloReply value) {
        System.out.println("Ответ: " + value.getMessage());
    }
    @Override
    public void onError(Throwable t) {
        System.err.println("Ошибка: " + t.getMessage());
    }
    @Override
    public void onCompleted() {
        System.out.println("Завершено");
    }
});

4.6. StreamObserver.onNext(V value)

Сигнатура: void onNext(V value)

Что делает: Вызывается для каждого полученного сообщения. В унарных вызовах вызывается один раз, в стриминге — несколько раз.

// Пример использования в серверном стриминге
@Override
public void listFeatures(FeatureRequest request, StreamObserver<Feature> responseObserver) {
    for (Feature feature : features) {
        responseObserver.onNext(feature);
    }
    responseObserver.onCompleted();
}

4.7. StreamObserver.onCompleted()

Сигнатура: void onCompleted()

Что делает: Сигнализирует, что поток данных завершен успешно. Должен вызываться после отправки всех сообщений.

responseObserver.onNext(reply);
responseObserver.onCompleted(); // Обязательно!

4.8. StreamObserver.onError(Throwable t)

Сигнатура: void onError(Throwable t)

Что делает: Вызывается при возникновении ошибки в потоке. После вызова этого метода поток считается завершенным.

responseObserver.onError(new StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Некорректный запрос")));

4.9. StatusRuntimeException

Сигнатура: public StatusRuntimeException(Status status)

Что делает: Исключение, которое содержит статус gRPC (код и описание). Используется для передачи ошибок между сервером и клиентом.

// Сервер выбрасывает ошибку
throw new StatusRuntimeException(Status.NOT_FOUND.withDescription("Пользователь не найден"));

// Клиент обрабатывает
try {
    stub.getUser(request);
} catch (StatusRuntimeException e) {
    if (e.getStatus().getCode() == Status.Code.NOT_FOUND) {
        System.out.println("Пользователь не найден");
    }
}

4.10. Context.current().withCancellation()

Сигнатура: public Context withCancellation()

Что делает: Создает новый контекст, который может быть отменен. Позволяет отменять RPC вызовы из другого потока.

Context.CancellableContext context = Context.current().withCancellation();
context.run(() ->                    

Рекомендаций