Welcome to my GitHub

https://github.com/zq2599/blog_demos

Content: Classification and summary of all original articles and supporting source code, involving Java, Docker, Kubernetes, DevOPS, etc.;

"Java version of gRPC combat" full series of links

  1. uses proto to generate code
  2. service release and call
  3. server stream
  4. client stream
  5. Two-way flow
  6. client dynamically obtains the server address
  7. Based on eureka registration discovery

About the four types defined by gRPC

This article is the third in the "java version of gRPC combat" series. In the previous article, we have experienced simple RPC request and response in actual combat. The simple request response method is actually only one of the four types defined by gRPC. Here is the "gRPC official The Chinese version of the document describes these four types of gRPC:

  1. Simple RPC: The client uses a stub to send a request to the server and wait for the response to return, just like a normal function call;
  2. Server-side streaming RPC: The client sends a request to the server and gets a stream to read the returned message sequence. The client reads the returned stream until there is no message in it; (that is, the content of this article)
  3. Client streaming RPC: The client writes a sequence of messages and sends it to the server, also using streams. Once the client finishes writing the message, it waits for the server to finish reading and return its response;
  4. Two-way streaming RPC: Both parties use read and write streams to send a sequence of messages. The two streams operate independently, so the client and server can read and write in any order they like: for example, the server can wait to receive all client messages before writing the response, or can alternately read and write messages, or other The combination of reading and writing. The sequence of messages in each stream is reserved;

Overview of this article

This article is the actual combat of server-side stream type gRPC services, including the following:

  1. Develop a gRPC service, the type is server-side flow;
  2. Develop a client to call the gRPC service released earlier;
  3. verify;
  4. Not much to say, start to code;

Source download

nameLinkRemark
Project homepagehttps://github.com/zq2599/blog_demosThe project's homepage on GitHub
git warehouse address (https)https://github.com/zq2599/blog_demos.gitThe warehouse address of the source code of the project, https protocol
git warehouse address (ssh)git@github.com:zq2599/blog_demos.gitThe warehouse address of the source code of the project, ssh protocol
  • There are multiple folders in this git project. The source code of the "java version of gRPC combat" series is under the <font color="blue">grpc-tutorials</font> folder, as shown in the red box below:

在这里插入图片描述

  • There are multiple directories under the <font color="blue">grpc-tutorials</font> folder. The server code for this article is in <font color="blue">server-stream-server-side</font > In the directory, the client code is in the <font color="blue">server-stream-client-side</font> directory, as shown below:

在这里插入图片描述

Develop a gRPC service, the type is server-side flow

  • The first thing to be developed is the gRPC server. There are seven things to do as shown in the figure below:

在这里插入图片描述

  • Open the <font color="blue">grpc-lib</font> module, and add a new file mall.proto under the <font color="red">src/main/proto</font> directory, and one of them is set The contents of the gRPC method ListOrders and its input and return objects are as follows. Note that the return value should be modified with the keyword <font color="red">stream</font>, indicating that the interface type is a server-side stream:
syntax = "proto3";

option java_multiple_files = true;
// 生成java代码的package
option java_package = "com.bolingcavalry.grpctutorials.lib";
// 类名
option java_outer_classname = "MallProto";

// gRPC服务,这是个在线商城的订单查询服务
service OrderQuery {
    // 服务端流式:订单列表接口,入参是买家信息,返回订单列表(用stream修饰返回值)
    rpc ListOrders (Buyer) returns (stream Order) {}
}

// 买家ID
message Buyer {
    int32 buyerId = 1;
}

// 返回结果的数据结构
message Order {
    // 订单ID
    int32 orderId = 1;
    // 商品ID
    int32 productId = 2;
    // 交易时间
    int64 orderTime = 3;
    // 买家备注
    string buyerRemark = 4;
}
  • Double-click generateProto in the red box in the figure below to generate java code based on proto:

在这里插入图片描述

  • The newly generated java code is shown in the red box as shown in the figure below:

在这里插入图片描述

  • Create a new module named <font color="blue">server-stream-server-side</font> under the parent project <font color="blue">grpc-turtorials</font>, and its build.gradle content is as follows :
// 使用springboot插件
plugins {
    id 'org.springframework.boot'
}

dependencies {
    implementation 'org.projectlombok:lombok'
    implementation 'org.springframework.boot:spring-boot-starter'
    // 作为gRPC服务提供方,需要用到此库
    implementation 'net.devh:grpc-server-spring-boot-starter'
    // 依赖自动生成源码的工程
    implementation project(':grpc-lib')
}
  • Create a new configuration file application.yml:
spring:
  application:
    name: server-stream-server-side
# gRPC有关的配置,这里只需要配置服务端口号
grpc:
  server:
    port: 9899
  • Startup class:
package com.bolingcavalry.grpctutorials;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ServerStreamServerSideApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServerStreamServerSideApplication.class, args);
    }
}
  • Next is the most critical gRPC service. The code is as follows. It can be seen that the responseObserver.onNext method is called multiple times to continuously output data to the client. Finally, the output is ended by responseObserver.onCompleted:
package com.bolingcavalry.grpctutorials;

import com.bolingcavalry.grpctutorials.lib.Buyer;
import com.bolingcavalry.grpctutorials.lib.Order;
import com.bolingcavalry.grpctutorials.lib.OrderQueryGrpc;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;
import java.util.ArrayList;
import java.util.List;

@GrpcService
public class GrpcServerService extends OrderQueryGrpc.OrderQueryImplBase {

    /**
     * mock一批数据
     * @return
     */
    private static List<Order> mockOrders(){
        List<Order> list = new ArrayList<>();
        Order.Builder builder = Order.newBuilder();

        for (int i = 0; i < 10; i++) {
            list.add(builder
                    .setOrderId(i)
                    .setProductId(1000+i)
                    .setOrderTime(System.currentTimeMillis()/1000)
                    .setBuyerRemark(("remark-" + i))
                    .build());
        }

        return list;
    }

    @Override
    public void listOrders(Buyer request, StreamObserver<Order> responseObserver) {
        // 持续输出到client
        for (Order order : mockOrders()) {
            responseObserver.onNext(order);
        }
        // 结束输出
        responseObserver.onCompleted();
    }
}
  • At this point, the server-side development is complete, let's develop a springboot application as a client, and see how to remotely call the <font color="blue">listOrders</font> interface to get the data output by the responseObserver.onNext method;

Develop a client to call the gRPC service released earlier

  • The basic function of the client module is to provide a web interface, which internally calls the server's <font color="blue">listOrders</font> interface, and returns the obtained data to the front end, as shown in the following figure:

在这里插入图片描述

  • Create a new module named <font color="blue">server-stream-client-side</font> under the parent project <font color="blue">grpc-turtorials</font>, and its build.gradle content is as follows :
plugins {
    id 'org.springframework.boot'
}

dependencies {
    implementation 'org.projectlombok:lombok'
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'net.devh:grpc-client-spring-boot-starter'
    implementation project(':grpc-lib')
}
  • The content of application configuration information application.yml is as follows, it can be seen that it is the configuration of the port and gRPC server address:
server:
  port: 8081
spring:
  application:
    name: server-stream-client-side

grpc:
  client:
    # gRPC配置的名字,GrpcClient注解会用到
    server-stream-server-side:
      # gRPC服务端地址
      address: 'static://127.0.0.1:9899'
      enableKeepAlive: true
      keepAliveWithoutCalls: true
      negotiationType: plaintext
  • The Order object returned by the <font color="blue">listOrders</font> interface on the server side contains a lot of gRPC-related content, which is not suitable for the return value of the web interface, so define a DispOrder class as the return value of the web interface:
package com.bolingcavalry.grpctutorials;

import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.Serializable;

@Data
@AllArgsConstructor
public class DispOrder {
    private int orderId;
    private int productId;
    private String orderTime;
    private String buyerRemark;
}
  • The bland start-up class:
package com.bolingcavalry.grpctutorials;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ServerStreamClientSideApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServerStreamClientSideApplication.class, args);
    }
}
  • Here comes the important point, GrpcClientService.java, which shows how to remotely call the listOrders interface of the gRPC service. It can be seen that for the server-side stream type interface, the client side will get <font color="blue">Iterator</font through the stub call. >Type of return value, the next thing to do is to traverse the Iterator:
package com.bolingcavalry.grpctutorials;

import com.bolingcavalry.grpctutorials.lib.Buyer;
import com.bolingcavalry.grpctutorials.lib.Order;
import com.bolingcavalry.grpctutorials.lib.OrderQueryGrpc;
import io.grpc.StatusRuntimeException;
import lombok.extern.slf4j.Slf4j;
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.springframework.stereotype.Service;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

@Service
@Slf4j
public class GrpcClientService {

    @GrpcClient("server-stream-server-side")
    private OrderQueryGrpc.OrderQueryBlockingStub orderQueryBlockingStub;

    public List<DispOrder> listOrders(final String name) {
        // gRPC的请求参数
        Buyer buyer = Buyer.newBuilder().setBuyerId(101).build();

        // gRPC的响应
        Iterator<Order> orderIterator;

        // 当前方法的返回值
        List<DispOrder> orders = new ArrayList<>();

        // 通过stub发起远程gRPC请求
        try {
            orderIterator = orderQueryBlockingStub.listOrders(buyer);
        } catch (final StatusRuntimeException e) {
            log.error("error grpc invoke", e);
            return new ArrayList<>();
        }

        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        log.info("start put order to list");
        while (orderIterator.hasNext()) {
            Order order = orderIterator.next();

            orders.add(new DispOrder(order.getOrderId(),
                                    order.getProductId(),
                                    // 使用DateTimeFormatter将时间戳转为字符串
                                    dtf.format(LocalDateTime.ofEpochSecond(order.getOrderTime(), 0, ZoneOffset.of("+8"))),
                                    order.getBuyerRemark()));
            log.info("");
        }

        log.info("end put order to list");

        return orders;
    }
}
  • Finally, make a controller class to provide a web interface to the outside, which will call the method of GrpcClientService:
package com.bolingcavalry.grpctutorials;

import com.bolingcavalry.grpctutorials.lib.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;

@RestController
public class GrpcClientController {

    @Autowired
    private GrpcClientService grpcClientService;

    @RequestMapping("/")
    public List<DispOrder> printMessage(@RequestParam(defaultValue = "will") String name) {
        return grpcClientService.listOrders(name);
    }
}
  • At this point, the encoding is complete, and the verification begins

verify

  1. Start <font color="blue">server-stream-server-side</font>, after successful startup, it will monitor port 9989:

在这里插入图片描述

  1. Start <font color="blue">server-stream-client-side</font>, and then visit the browser: http://localhost:8081/?name=Tom , the result is as follows (firefox automatically format json data), it can be seen that the remote data of gRPC has been successfully obtained:

在这里插入图片描述

At this point, the development and use of the server-side flow-type gRPC interface is completed, and the next chapters will continue to learn the other two types;

You are not alone, Xinchen and original are with you all the way

  1. Java series
  2. Spring series
  3. Docker series
  4. kubernetes series
  5. database + middleware series
  6. DevOps series

Welcome to pay attention to the public account: programmer Xin Chen

Search "Programmer Xin Chen" on WeChat, I am Xin Chen, and I look forward to traveling the Java world with you...
https://github.com/zq2599/blog_demos

程序员欣宸
147 声望24 粉丝

热爱Java和Docker