В моем случае я использую многомодульный проект, но можно создать отдельный проект. Структура проекта будет примерно следующей:
![Структура проекта Структура проекта](https://habrastorage.org/getpro/habr/upload_files/26b/dd8/2cb/26bdd82cbce631795eb2660f5a69055f.png)
Мы реализуем основные методы, которые были описаны в .proto. Обратите внимание, что serverStream разобьется на 2 метода, о чем я расскажу дальше.
![Эти методы мы реализуем в ходе статьи Эти методы мы реализуем в ходе статьи](https://habrastorage.org/getpro/habr/upload_files/5d2/235/0f6/5d22350f62aeac80f7fcae0922317650.png)
Как и в прошлой статье нам необходимо добавить плагин для генерации .proto файлов. Если возникли сложности, можно посмотреть код примера в репозитории.
Для того, чтобы отправлять вызовы на удаленный сервер, нам необходимо создать клиента. gRPC предоставляет несколько возможностей создания клиента - это BlockingStub и Stub. В чем отличие этих “заглушек”? BlockingStub - создает новую блокирующую заглушку, которая поддерживает вызовы унарного и потокового вывода (unary and streaming output calls, офф. док.), Stub - cоздает новую асинхронную заглушку, которая поддерживает все типы вызовов. Мы рассмотрим оба варианта заглушек. Для создании заглушки нам необходим Channel. Channel - если коротко, то это виртуальное соединение с концептуальной конечной точкой для выполнения RPC. Для создания Channel нам потребуется хост и порт.
// Access a service running on the local machine on port 7777
String target = "localhost:7777";
// Create a communication channel to the server, known as a Channel. Channels are thread-safe
// and reusable. It is common to create channels at the beginning of your application and reuse
// them until the application shuts down.
ManagedChannel channel = ManagedChannelBuilder.forTarget(target)
// Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
// needing certificates.
.usePlaintext()
.build();
Затем мы можем создать newBlockingStub и newStub (создаем через Channel и ManagedChannel для примера)
// 'channel' here is a Channel, not a ManagedChannel,
// so it is not this code's responsibility to
// shut it down.
// Passing Channels to code makes code easier to test
// and makes it easier to reuse Channels.
blockingStub = ProfileServiceGrpc.newBlockingStub(channel);
asyncStub = ProfileServiceGrpc.newStub(ManagedChannelBuilder.forAddress(HOST, PORT).usePlaintext().build());
Вся подготовительная работа закончена. Теперь можно реализовать методы интерфейса. Начнем с самого простого - getCurrentProfile. Для вызова сервера, нам нужно лишь вызвать метод заглушки и передать в нее необходимые параметры.
![Реализация getCurrentProfile Реализация getCurrentProfile](https://habrastorage.org/getpro/habr/upload_files/0b6/f8e/7de/0b6f8e7de6272f462e50e8ba085e04cd.png)
Я использую стандартный main метод, где создаю объект интерфейса и вызываю необходимые методы. В целом ничего не мешает написать тесты, но для меня быстрее и проще написать main.
![Main Main](https://habrastorage.org/getpro/habr/upload_files/c38/e8a/7cb/c38e8a7cb0bc4b01b83db3ecba5420f7.png)
Теперь если вызвать getCurrentProfile, то мы получим ответ профиля с именем “test” (сервер из прошлой статьи).
![](https://habrastorage.org/getpro/habr/upload_files/92a/594/ba5/92a594ba5f0d807a7bb0a65709d3721b.png)
Отлично, с базовым методом разобрались. Давайте теперь рассмотрим clientStream. Отличия от предыдущего метода минимальные. Нам лишь нужно передавать сообщения на сервер не единожды, как было в прошлом примере, а постоянно. Если обратиться к логам сервера, то можно заметить, что он получает сообщения от клиента и логирует их.
Server Stream. Для реализации получения потоковых данных с сервера, можно использовать различные заглушки. Если использовать блокирующую заглушку, то мы дождемся всех ответов сервера и затем обработаем результат. Если использовать асинхронную заглушку, то мы будем обрабатывать результат сразу после того как сервер нам ответит.
// Блокирующая и асинхронная заглушки
private final ProfileServiceGrpc.ProfileServiceBlockingStub blockingStub;
private final ProfileServiceGrpc.ProfileServiceStub asyncStub;
// Создание заглушек в конструкторе
public GrpcProfileClientImpl() {
// Create a communication channel to the server, known as a Channel. Channels are thread-safe
// and reusable. It is common to create channels at the beginning of your application and reuse
// them until the application shuts down.
ManagedChannel channel = ManagedChannelBuilder.forTarget(TARGET)
// Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
// needing certificates.
.usePlaintext()
.build();
blockingStub = ProfileServiceGrpc.newBlockingStub(channel);
asyncStub = ProfileServiceGrpc.newStub(channel);
}
Для принятия ответов, используя BlockingStub нам потребуется Iterator, куда сервер будет складывать ответы. И потом, пробежавшись по нему, мы увидим то, что нам прислал сервер.
![](https://habrastorage.org/getpro/habr/upload_files/a98/a3f/473/a98a3f473d2053f6a9ed1ed0e0ded4cb.png)
А вот для не блокирующего принятия сообщений нам потребуется передать StreamObserver, в который сервер будет передавать ответы.
![Не блокирующее получение ответов от сервера Не блокирующее получение ответов от сервера](https://habrastorage.org/getpro/habr/upload_files/725/f72/4e8/725f724e8784d33fdda5d83ce0667599.png)
Так как у нас получение происходит асинхронно, я добавляю CountDownLatch с await, для того что бы текущий поток подождал ответов от сервера.
И последний вариант клиент серверного взаимодействия - biDirectionalStream. Нам снова потребуется реализовать StreamObserver в который клиент будет передавать сообщения и получать ответы от сервера.
![](https://habrastorage.org/getpro/habr/upload_files/34c/20f/835/34c20f83532d140dc55b2b7106a73d6a.png)
Если все запустить и проверить, то по логам клиента и сервера можно заметить, что сообщение передаются и наш клиент работает корректно.
Подведем итоги
Мы написали клиент, для рассмотренного в прошлой статье сервера на gRPC. Рассмотрели и реализовали основные варианты клиент серверного взаимодействия с использованием gRPC. А вы используете gRPC для клиент серверного общения? Почему вы выбрали gRPC?
В прошлой статье мы разобрали, как писать сервер на gRPC. И протестировали его с помощью BloomRPC. Теперь, давайте разберем как пишется клиент. И попробуем отправлять запросы с клиента на сервер. Это будет наш первый шаг к созданию микросервисов на грпц.