gRpc指南

2023-11-09

本文翻译自官网。原文:https://grpc.io/docs/languages/java/quickstart/

快速开始

下面通过一个简单的样例,让你快速上手基于java的gRpc的使用。

前置条件

  • JDK7以上版本

获取示例代码

示例代码是grpc-java的一部分。

  1. 从github仓库下载gprc代码压缩文件并解压,或者直接克隆代码:
    $ git clone -b v1.45.1 --depth 1 https://github.com/grpc/grpc-java
  2. 进入示例代码路径:
    $ cd gprc-java/examples

运行示例

在示例代码路径下:

编译客户端和服务端
$ ./gradlew installDist

运行服务端

$ ./build/install/examples/bin/hello-world-server
INFO: Server started, listening on 50051

打开另一个命令行终端,运行客户端

$ ./build/install/examples/bin/hello-world-client
INFO: Will try to greet world ...
INFO: Greeting: Hello world

至此,一个client-server模式的gRPC应用就算运行起来了。

更新gRPC服务

在这一部分,你将通过添加另外的服务方法来更新应用。
gRpc服务通过protocol buffers来定义。你需要知道的是,服务端和客户端存根都有一个SayHello()的RPC方法,该方法接受客户端的HelloRequest请求参数,并由服务端返回HelloReply结果。方法定义如下:

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

打开src/main/proto/helloworld.proto添加一个新方法SayHelloAgain(), 请求参数和返回结果同样使用原来的类型:

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  // Sends another greeting
  rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

更新app

当你生成样例的时候,构建过程会生成GreeterGrpc.java,该文件包含系统生成的gRpc客户端和服务端类文件。除此之外,还会生成其他的序列化,检索请求和返回类型的代码文件。
开发人员只需要关注方法实现并发起请求调用,而无需关注系统自动实现的代码框架。

这个和通过idl文件生成代码存根是一样的

更新服务端

在相同路径下,打开src/main/java/io/grpc/examples/helloworld/HelloWorldServer.java,实现新的方法:

private class GreeterImpl extends GreeterGrpc.GreeterImplBase {

  @Override
  public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
    HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
    responseObserver.onNext(reply);
    responseObserver.onCompleted();
  }

  @Override
  public void sayHelloAgain(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
    HelloReply reply = HelloReply.newBuilder().setMessage("Hello again " + req.getName()).build();
    responseObserver.onNext(reply);
    responseObserver.onCompleted();
  }
}

更新客户端

同样的,修改src/main/java/io/grpc/examples/helloworld/HelloWorldClient.java文件:

public void greet(String name) {
  logger.info("Will try to greet " + name + " ...");
  HelloRequest request = HelloRequest.newBuilder().setName(name).build();
  HelloReply response;
  try {
    response = blockingStub.sayHello(request);
  } catch (StatusRuntimeException e) {
    logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
    return;
  }
  logger.info("Greeting: " + response.getMessage());
  try {
    response = blockingStub.sayHelloAgain(request);
  } catch (StatusRuntimeException e) {
    logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
    return;
  }
  logger.info("Greeting: " + response.getMessage());
}

运行修改后的app

像上文一样运行服务端和客户端,分别执行以下命令:
首先编译代码

$ ./gradlew installDist

接着运行服务端

$ ./build/install/examples/bin/hello-world-server

再运行客户端

$ ./build/install/examples/bin/hello-world-client
INFO: Will try to greet world ...
INFO: Greeting: Hello world
INFO: Greeting: Hello again world

gRpc介绍

通过本章节,你可以了解到如何定义接口文件,如何生成服务端和客户端系统代码,何如实现简单的分布式服务。

在阅读本章节之前,假设你已经熟悉了protocol buffers

注意,本章节的示例中使用的protocol buffers版本是proto3。如果你想了解更多,请参考proto3 language guide.

为什么要使用gRPC

示例代码是一个简单的路由映射程序。

通过gRPC,我们可以定义.proto文件,然后生成gPRC支持的任何其他语言的客户端和服务端系统代码。这些代码既可以运行在大型服务器上,也可以运行在你自己的平板电脑上。
换句话说,gRPC帮你屏蔽了不同语言,不同环境之间的通讯细节,使得开发人员只需要关注到业务逻辑的实现上,提升了开发效率。

另外,我们也能充分利用protocol buffers的优点,包括高效的序列化效率,简单的IDL文件和便利地接口更新。

示例代码和设置

示例代码路径:/grpc-java/examples/src/main/java/io/grpc/examples/routeguide

进入examples路径:

cd grpc-java/examples

服务定义

首先需要通过protocol buffers来声明接口、请求参数和返回参数的类型。具体参见:grpc-java/examples/src/main/proto/route_guide.proto。其中包含以下语句,用来指定生成的服务端和客户端系统代码的文件包名:

option java_package = "io.grpc.examples.routeguide";

如果不指定该选项,则默认使用package指定的包名。当然,对于非java语言,option java_package是无效的。

服务定义如下:

service RouteGuide {
    ...
}

接下来在service里面定义方法,指定请求和返回类型。

简单rpc

客户端发起请求,服务端处理请求并返回结果,就像正常的函数调用一样。

// Obtains the feature at a given position.
rpc GetFeature(Point) returns (Feature) {}

服务端流rpc

客户端发起请求并得到一个stream返回,循环从stream中读取有序数据直到没有更多的数据为止。

服务端流rpc需要在返回类型前面加上stream关键字。

// Obtains the Features available within the given Rectangle.  Results are
// streamed rather than returned at once (e.g. in a response message with a
// repeated field), as the rectangle may cover a large area and contain a
// huge number of features.
rpc ListFeatures(Rectangle) returns (stream Feature) {}

客户端流rpc

客户端以流式方式将数据发送到服务端,当全部发送完成后,等待服务端的返回。服务端方面持续读取请求数据,读取完所有数据后进行处理并一次性返回结果。

客户端流rpc需要在请求类型前面加上stream关键字。

// Accepts a stream of Points on a route being traversed, returning a
// RouteSummary when traversal is completed.
rpc RecordRoute(stream Point) returns (RouteSummary) {}

双向流(bidirecectional)rpc

客户端请求流式发送数据,服务端返回也是流式返回数据。客户端的请求流和服务端返回流是独立的,这意味着客户端和服务端可以以它们想要的方式来读或者写数据。比如服务端可以等待所有数据接收完成后再返回数据,也可以接受完一部分数据之后就立即返回,再继续接收数据并返回。甚至能以其他的组合方式来读写数据。

双向流rpc需要在请求和返回参数前面都加上stream关键字。

// Accepts a stream of RouteNotes sent while a route is being traversed,
// while receiving other RouteNotes (e.g. from other users).
rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}

当然,接口涉及的请求类型和返回类型也会在proto文件中定义,比如Point 的定义:

message Point {
  int32 latitude = 1;
  int32 longitude = 2;
}

生成客户端和服务端存根代码

定义好了接口后就能通过protocol buffer编译器(protoc)来生成客户端和服务端的系统代码了(当然,不同的语言还需要各自不同的插件)。

如果在项目中使用Gradle或者Maven,可以将protoc编译器放到build过程中,具体参考:grpc-java README

生成的系统代码包括:Feature.java, Point.java, Rectangle.java为输入输出的数据类型;RouteGuideGrpc.java为服务端功能实现类;客户端调用类。如下所示:

创建server

首先让我们看一看如何创建RouteGuide服务端:

  • 覆写服务端功能实现基类,也就是上面提到的RouteGuideGrpc类,该类是由系统生成的。
  • 运行服务端,监听客户端请求并返回。

服务端实现类:grpc-java/examples/src/main/java/io/grpc/examples/routeguide/RouteGuideServer.java。

RouteGuide功能实现

如代码所示,服务端实现类RouteGuideService继承了系统生成的基类RouteGuideGrpc.RouteGuideImplBase:

private static class RouteGuideService extends RouteGuideGrpc.RouteGuideImplBase {
 ...
}
简单rpc

RouteGuideService实现了服务定义中的所有方法。

简单rpc接口如GetFeature(),该方法请求参数为位置Point,返回该位置对应的Feature。如下所示:

@Override
public void getFeature(Point request, StreamObserver<Feature> responseObserver) {
  responseObserver.onNext(checkFeature(request));
  responseObserver.onCompleted();
}

...

private Feature checkFeature(Point location) {
  for (Feature feature : features) {
    if (feature.getLocation().getLatitude() == location.getLatitude()
        && feature.getLocation().getLongitude() == location.getLongitude()) {
      return feature;
    }
  }

  // No feature was found, return an unnamed feature.
  return Feature.newBuilder().setName("").setLocation(location).build();
}

请求参数为两个: 位置Point和响应观察器StreamObserver。通过响应观察期的onNext()来返回Feature,然后通过onCompleted() 来表明本次rpc调用已完成。

服务端流rpc

ListFeatures是一个服务端流rpc接口,返回多个Feature。代码如下所示:

private final Collection<Feature> features;

...
@Override
public void listFeatures(Rectangle request, StreamObserver<Feature> responseObserver) {
  int left = min(request.getLo().getLongitude(), request.getHi().getLongitude());
  int right = max(request.getLo().getLongitude(), request.getHi().getLongitude());
  int top = max(request.getLo().getLatitude(), request.getHi().getLatitude());
  int bottom = min(request.getLo().getLatitude(), request.getHi().getLatitude());

  for (Feature feature : features) {
    if (!RouteGuideUtil.exists(feature)) {
      continue;
    }

    int lat = feature.getLocation().getLatitude();
    int lon = feature.getLocation().getLongitude();
    if (lon >= left && lon <= right && lat >= bottom && lat <= top) {
      responseObserver.onNext(feature);
    }
  }
  responseObserver.onCompleted();
}

在for循环中获取符合条件的Feature返回。循环结束后通过onCompleted()表明本次rpc调用结束。

客户端流rpc

下面看一个更复杂一点的客户端流rpc接口:RecordRoute(),请求数据是流式的,返回一个RouteSunmmary。

@Override
public StreamObserver<Point> recordRoute(final StreamObserver<RouteSummary> responseObserver) {
  return new StreamObserver<Point>() {
    int pointCount;
    int featureCount;
    int distance;
    Point previous;
    long startTime = System.nanoTime();

    @Override
    public void onNext(Point point) {
      pointCount++;
      if (RouteGuideUtil.exists(checkFeature(point))) {
        featureCount++;
      }
      // For each point after the first, add the incremental distance from the previous point
      // to the total distance value.
      if (previous != null) {
        distance += calcDistance(previous, point);
      }
      previous = point;
    }

    @Override
    public void onError(Throwable t) {
      logger.log(Level.WARNING, "Encountered error in recordRoute", t);
    }

    @Override
    public void onCompleted() {
      long seconds = NANOSECONDS.toSeconds(System.nanoTime() - startTime);
      responseObserver.onNext(RouteSummary.newBuilder().setPointCount(pointCount)
          .setFeatureCount(featureCount).setDistance(distance)
          .setElapsedTime((int) seconds).build());
      responseObserver.onCompleted();
    }
  };
}

在服务端流rpc中,返回结果是流式的,而客户端流rpc中,请求数据是流式的。在该方法中,实例化了一个匿名类对象来返回,并在该匿名类中覆写了几个方法:

  • 覆写onNext(),该方法用于接收客户端请求并进行处理。
  • 覆写onCompleted(),当服务端接收完客户端的所有请求数据之后,该方法会被调用,构建一个RouteSummary对象。然后调用响应监听器的onNext()onCompleted()来返回数据和结束rpc调用。
双向rpc

最后是双向rpc接口:routeChat()

@Override
public StreamObserver<RouteNote> routeChat(final StreamObserver<RouteNote> responseObserver) {
     return new StreamObserver<RouteNote>() {
       @Override
       public void onNext(RouteNote note) {
         List<RouteNote> notes = getOrCreateNotes(note.getLocation());

         // Respond with all previous notes at this location.
         for (RouteNote prevNote : notes.toArray(new RouteNote[0])) {
           responseObserver.onNext(prevNote);
         }

         // Now add the new note to the list
         notes.add(note);
       }

       @Override
       public void onError(Throwable t) {
         logger.log(Level.WARNING, "routeChat cancelled");
       }

       @Override
       public void onCompleted() {
         responseObserver.onCompleted();
       }
     };
   }

就像客户端流rpc一样,双向流rpc需要两个StreamObserver对象,一个用于从客户端读数据,一个用于向客户端写数据。区别在于:双向流rpc的读和写是同时存在的。

启动server

启动Server的代码如下:

public RouteGuideServer(int port, URL featureFile) throws IOException {
      this(ServerBuilder.forPort(port), port, RouteGuideUtil.parseFeatures(featureFile));
}

// Create a RouteGuide server using serverBuilder as a base and features as data.
public RouteGuideServer(ServerBuilder<?> serverBuilder, int port, Collection<Feature> features) {
      this.port = port;
      server = serverBuilder.addService(new RouteGuideService(features)).build();
}
...
public void start() throws IOException {
      server.start();
      logger.info("Server started, listening on " + port);
...
}

如代码所示,我们通过ServerBuilder来构建并启动服务端:

  • 指定服务端地址和端口,通过builder的forPort()来监听客户端请求。
  • 创建一个服务实现类的实例RouteGuideService,并将其作为参数传递给builder的addService()方法中。
  • 调用build()start()方法来创建和启动rpc服务端。

创建client

在这一部分,我们将看看如何创建RouteGuide的客户端。完整的代码路径:
grpc-java/examples/src/main/java/io/grpc/examples/routeguide/RouteGuideClient.java.

实例化stub

为了调用服务方法,首先需要创建一个stub或者更确切的说,两个stub。一个是阻塞/同步的stub,一个是非阻塞/异步的stub。

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

/** Construct client for accessing RouteGuide server using the existing channel. */
public RouteGuideClient(ManagedChannelBuilder<?> channelBuilder) {
  channel = channelBuilder.build();
  blockingStub = RouteGuideGrpc.newBlockingStub(channel); // 同步存根
  asyncStub = RouteGuideGrpc.newStub(channel);  // 异步存根
}

调用服务方法

简单rpc

在阻塞stub上调用简单的rpc GetFeature与调用本地方法一样简单。

Point request = Point.newBuilder().setLatitude(lat).setLongitude(lon).build();
Feature feature;
try {
  feature = blockingStub.getFeature(request);
} catch (StatusRuntimeException e) {
  logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
  return;
}

我们创建并输入一个请求协议缓冲对象(例子中的Point),将其作为参数传给getFeature()方法。如果调用过程中出现错误,将抛出StatusRuntimeException异常。

服务流rpc

下面是服务端流rpc ListFeatures的客户端调用:

Rectangle request =
    Rectangle.newBuilder()
        .setLo(Point.newBuilder().setLatitude(lowLat).setLongitude(lowLon).build())
        .setHi(Point.newBuilder().setLatitude(hiLat).setLongitude(hiLon).build()).build();
Iterator<Feature> features;
try {
  features = blockingStub.listFeatures(request);
} catch (StatusRuntimeException e) {
  logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
  return;
}

客户端的调用实际上与简单rpc差不多,唯一的差别在于服务端流rpc的返回结果是持续不断的,因此需要通过迭代器来接收,然后再从迭代器中取出所有的返回结果。

客户端流rpc

在客户端流rpc接口RecordRoute()中,我们流式发送Point到服务端,并得到一个RouteSummary结果。在这个方法中,我们需要使用异步stub。如下所示:

public void recordRoute(List<Feature> features, int numPoints) throws InterruptedException {
  info("*** RecordRoute");
  final CountDownLatch finishLatch = new CountDownLatch(1);
  StreamObserver<RouteSummary> responseObserver = new StreamObserver<RouteSummary>() {
    @Override
    public void onNext(RouteSummary summary) {
      info("Finished trip with {0} points. Passed {1} features. "
          + "Travelled {2} meters. It took {3} seconds.", summary.getPointCount(),
          summary.getFeatureCount(), summary.getDistance(), summary.getElapsedTime());
    }

    @Override
    public void onError(Throwable t) {
      Status status = Status.fromThrowable(t);
      logger.log(Level.WARNING, "RecordRoute Failed: {0}", status);
      finishLatch.countDown();
    }

    @Override
    public void onCompleted() {
      info("Finished RecordRoute");
      finishLatch.countDown();
    }
  };

  StreamObserver<Point> requestObserver = asyncStub.recordRoute(responseObserver);
  try {
    // Send numPoints points randomly selected from the features list.
    Random rand = new Random();
    for (int i = 0; i < numPoints; ++i) {
      int index = rand.nextInt(features.size());
      Point point = features.get(index).getLocation();
      info("Visiting point {0}, {1}", RouteGuideUtil.getLatitude(point),
          RouteGuideUtil.getLongitude(point));
      requestObserver.onNext(point);
      // Sleep for a bit before sending the next one.
      Thread.sleep(rand.nextInt(1000) + 500);
      if (finishLatch.getCount() == 0) {
        // RPC completed or errored before we finished sending.
        // Sending further requests won't error, but they will just be thrown away.
        return;
      }
    }
  } catch (RuntimeException e) {
    // Cancel RPC
    requestObserver.onError(e);
    throw e;
  }
  // Mark the end of requests
  requestObserver.onCompleted();

  // Receiving happens asynchronously
  finishLatch.await(1, TimeUnit.MINUTES);
}

为了调用这一方法,我们需要创建一个StreamObserver,并覆写一些方法:

  • onNext():当服务端返回RouteSummary时,该方法负责将其输出。
  • onCompleted():当服务端返回数据时,该方法被调用,判断服务端数据是否写完。

从上文可以看出,客户端流方法调用需要实现两个StreamObserver,一个用于发送客户端请求,一个用于接收服务端的返回。

双向流rpc

双向流rpc接口RouteChat()客户端调用如下:

public void routeChat() throws Exception {
  info("*** RoutChat");
  final CountDownLatch finishLatch = new CountDownLatch(1);
  StreamObserver<RouteNote> requestObserver =
      asyncStub.routeChat(new StreamObserver<RouteNote>() {
        @Override
        public void onNext(RouteNote note) {
          info("Got message \"{0}\" at {1}, {2}", note.getMessage(), note.getLocation()
              .getLatitude(), note.getLocation().getLongitude());
        }

        @Override
        public void onError(Throwable t) {
          Status status = Status.fromThrowable(t);
          logger.log(Level.WARNING, "RouteChat Failed: {0}", status);
          finishLatch.countDown();
        }

        @Override
        public void onCompleted() {
          info("Finished RouteChat");
          finishLatch.countDown();
        }
      });

  try {
    RouteNote[] requests =
        {newNote("First message", 0, 0), newNote("Second message", 0, 1),
            newNote("Third message", 1, 0), newNote("Fourth message", 1, 1)};

    for (RouteNote request : requests) {
      info("Sending message \"{0}\" at {1}, {2}", request.getMessage(), request.getLocation()
          .getLatitude(), request.getLocation().getLongitude());
      requestObserver.onNext(request);
    }
  } catch (RuntimeException e) {
    // Cancel RPC
    requestObserver.onError(e);
    throw e;
  }
  // Mark the end of requests
  requestObserver.onCompleted();

  // Receiving happens asynchronously
  finishLatch.await(1, TimeUnit.MINUTES);
}

与客户端流rpc类似,双向流rpc也有两个StreamObserver,一个用于发送请求,一个用于接收响应。区别在于:双向流rpc的读和写是同时存在的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

gRpc指南 的相关文章

  • 张量流服务

    有谁知道如何创建用于张量流服务的 C 客户端 我的张量流服务安装 我使用张量流服务 dockerfile 安装了张量流服务 然后在容器内执行了以下操作 pip install tensorflow pip install tensorflo
  • 是否可以使用java客户端使用InProcessChannel在同一进程中调用c++服务器?

    grpc java 和 grpc c 都支持进程内通道 如果 grpc 客户端和服务器在同一进程中 则使用该通道 所以我有一个问题 是否可以使用java客户端使用进程内通道在同一进程中调用c 服务器 不会 每种语言的进程内通道都是特定于语言
  • Docker compose找不到本地包

    因此 我在 GOlang 中创建了 REST api 和一个 grpc 服务 现在我想在 docker compose 中组合它 我的 docker compose 看起来像这样 version 3 services db image po
  • Channel ManagedChannelImpl 未正确关闭

    如果我运行这两个测试 我会收到错误 1st test Rule public GrpcCleanupRule grpcCleanup new GrpcCleanupRule Test public void findAll throws E
  • XXX_* 输入生成的 *.pb.go 文件

    我正在研究一个tutorial https ewanvalentine io microservices in golang part 1 关于 gRPC 当我生成 pb go文件 我得到一些XXX 输入我的结构 这是我的consignme
  • 用于远程IP(主机)的Java RMI

    我是新手 我无法理解RMI正确 互联网上有大量的教程 但据我所知 它们都是针对本地主机的 服务器和客户端都运行在同一台机器上 我想在任何机器上运行客户端 并且主机将位于一台计算机上 让我们考虑一下IP 11 11 11 11 上1099 但
  • 了解 XDR 规范以创建 *.x 文件

    我浏览了互联网上的几个网站 实际上想知道我们如何在规范中编写规范 x文件以生成等效函数 通常 您可以从 x 文件生成三 组 代码 消息 xdr 编码器 解码器函数 客户端存根和服务器存根 当然 您也可以手动完成 但要做到正确的工作量太大 使
  • 如何对子目录内的服务进行 GRPC 调用? (在.Net框架中)

    我正在尝试连接到 NetFramework 4 5 Windows 应用程序中的 gRPC 服务 我目前正在使用元包 nuget Grpc 我知道它现在仅处于维护模式 我需要访问不在主机 端口位置但有子路径 子目录的服务 我的服务位于这里
  • NodeJs 错误 - 无法加载 gRPC 二进制模块,因为未为当前系统安装预期目录?

    我正在运行我的 NodeJs 应用程序 但收到错误 sudo usr local bin node app js Error Failed to load gRPC binary module because it was not inst
  • 如何通过RPC监听firestore

    我想听firestore中的实时变化 而且我也只允许使用Go 由于 Firestore SDK for Go 没有任何选项来监听实时更改 因此我决定使用 firestore v1beta1 sdk 我编写了以下代码来做到这一点 func T
  • 在 Windows 上链接 gRPC for Visual C++

    我正在尝试在 Visual C 项目中使用 gRPC 到目前为止我有 1 构建gRPC with vcpkg vcpkg install grpc x64 windows2 综合vcpgk带有视觉工作室的库 vcpkg integrate
  • Java gRPC 服务器用于长寿命流的有效实现

    我想了解 gRPC 框架的一部分 用于长期流的资源管理 假设我们有无限的罕见 每秒一次左右 事件源 我们希望通过 grpc 流的方式将其流式传输到客户端 这些事件由服务器上的单个应用程序线程生成 我看到两种可能的流事件实现 在 rpc 调用
  • Google 语音流 api 的 Grpc 代理服务器没有响应 ( Grpc.Core.RpcException )

    我们正在尝试实现 GRPC 代理服务器 ASP NET Core 控制台应用程序 它将传入数据代理到 Google Speech 流 api 代码与中几乎相同示例应用程序 https cloud google com speech docs
  • 微服务之间的通信

    假设您有微服务 A B 和 C 它们当前都通过 HTTP 进行通信 假设服务 A 向服务 B 发送请求 服务 B 得到响应 然后 该响应中返回的数据必须发送到服务 C 进行一些处理 然后最终返回到服务 A 服务 A 现在可以在网页上显示结果
  • Protobuf-java:合并两个原始对象而不连接重复字段

    我想合并两个复杂的原型对象protoA and protoB类型相同 如果两个对象中都设置了字段 则protoB应在生成的原型中设置 我可以使用 mergeFrom 功能 protoA toBuilder mergeFrom protoB
  • 如何将 Google proto 时间戳转换为 Java LocalDate?

    我们需要将 Google Protobuf 时间戳转换为正常日期 在这种情况下 有没有办法将 Google Proto 缓冲区时间戳转换为 JavaLocalDate直接地 tl dr 作为 UTC 时刻 转换为java time Inst
  • 在 grpc python 中处理异步流请求

    我试图了解如何使用双向流处理 grpc api 使用 Python API 假设我有以下简单的服务器定义 syntax proto3 package simple service TestService rpc Translate stre
  • 缺少节点-v59-linux-x64/grpc_node.node

    我正在尝试在我的服务器中使用 Firebase admin SDK 当我部署时 出现错误 我在 firebase admin node module 映射中缺少文件 node v59 linux x64 grpc node node 我在包
  • protoc-gen-js:程序未找到或不可执行

    尝试编译我的原型 我需要编译它们以在我的反应应用程序中使用 但我收到这个错误 我运行命令如下 cd src main proto protoc I proto js out import style commonjs grpc web ou
  • 我应该通过 gRPC 传输大型数据集而不需要手动分块吗?

    我想使用 gRPC 公开一个接口 用于在两个服务之间双向传输大型数据集 100 MB 由于 gRPC 默认施加 4 MB 消息大小限制 因此首选方法似乎是手动编码块流 并在接收端重新组装它们 1 https jbrandhorst com

随机推荐

  • react讲解(组件,生命周期以及受控组件)

    文章目录 前言 一 组件的通信原理 state 和 setState 二 组件分类 1 类组件 2 组件中父子组件的通信 代码示例 A组件代码 B组件代码 2 跨组件通信 A组件代码如下 C组件代码如下 三 组件的生命周期
  • Elasticsearch内存那些事儿

    Elasticsearch 内存分配设置详解 前言 该给 ES 分配多少内存 为什么是给 ES 分配服务器的一半内存 为什么内存使用率不断升高 没有释放 为何经常有某个 field 的数据量超出内存限制的异常 为何感觉上没多少数据 也会经常
  • TCP协议 ---可靠传输的各种机制

    目录 一 可靠 确认应答机制 保证数据可靠 有序的到达对端 超时重传机制 二 效率 2 1 提高自身发送数据量 滑动窗口机制 滑动窗口的发送缓冲区是环形队列 滑动窗口的大小会变化吗 滑动窗口的nb之处 滑动窗口丢包问题 2 2 对方的接受能
  • twrp3.3.0刷n9002_【笔刷】iPad Procreate5.0新笔刷分享,巨好用!

    01 Procreate 趣味复古蜡笔纹理笔刷15款 适用软件 Procreate5 0以上 适用系统 ipad系统 笔刷格式 brushset 素材大小 93MB 笔刷视频演示 赠送15款笔刷 29款色卡 一张纸张背景 02 Procre
  • 树莓派Pico

    9 2 在MS Windows上构建 在Microsoft Windows 10或Windows 11上安装工具链与其他平台有些不同 然而安装后 RP2040的构建代码基本类似 Tips 什么是工具链 工具链是指一系列软件 逐个使用这一系列
  • docker run中-v参数的用法解释

    作用 挂载宿主机的一个目录 如 docker run it v 宿主机目录 容器目录 镜像名 bin bash 这里 it是参数作用是 i 以交互模式运行容器 通常与 t 同时使用 t 为容器重新分配一个伪输入终端 通常与 i 同时使用 就
  • keil编译时候产生的错误(Error: L6200E: Symbol....)解决方法

    今天分享一个自己在做实验时候发现的一个错误问题 查了一下网上也有人遇到这样的问题 就拿出来分享了一下自己遇到的情况 首先看keil的错误提示 如图所示 可以看到两个报错为 Error L6200E Symbol usart3 init mu
  • CV Code

    点击我爱计算机视觉标星 更快获取CVML新技术 计算机视觉技术发展迅速 很多时候 可悲的不是我们没有努力 而是没有跟上时代的步伐 努力coding终于出来结果了 却发现早就有人开源了 效果还比自己写的好 CV君汇总了最近过去的一周新出的开源
  • 【Matlab】将Matlab里的几个变量一个存成csv文件

    注意 几个变量类型可以不同 但是长度必须相同 举例说明 1 比如说在workspace里已经有两个变量 a和b如图 每个变量为1列 想把这两个变量存成一个csv文件 2 先给这两个变量名起个名字 分别为A B 存入 datacolumns
  • 数据属性类型

    数据集由数据对象组成 一个数据对象代表一个实体 数据对象又称样本 实例 数据点或对象 属性 attribute 是一个数据字段 表示数据对象的一个特征 属性向量 或特征向量 是用来描述一个给定对象的一组属性 属性有不同类型 标称属性 nom
  • python面试题

    文章目录 Python面试基础题小汇总 1 Python是如何进行内存管理的 2 什么是lambda函数 它有什么好处 3 Python里面如何实现tuple和list的转换 4 请写出一段Python代码实现删除一个list里面的重复元素
  • 常用的运算放大器电路

    在线仿真网站 http scratch trtos com circuitjs html 一 反向比例放大电路 二 同向比例放大电路 三 电压跟随器 四 反向求和运算电路 五 同向求和运算电路 六 加减法运算放大器 七 差分放大器 八 积分
  • 关于自制CMSIS_DAP离线下载器下载算法的代码说明:“0xE00ABE00, 0x062D780D, 0x24084068, 0xD3000040, 0x1E644058, 0x1C49D1FA“

    关于自制CMSIS DAP离线下载器下载算法的代码说明 0xE00ABE00 0x062D780D 0x24084068 0xD3000040 0x1E644058 0x1C49D1FA 在自制CMSIS DAP离线下载器的时候 利用FLM
  • Mysql篇-第2章,什么是脏读、幻读、不可重复读?如何处理?

    一 Mysql进行事务并发控制时经常遇到的问题 脏读 在事务进行中 读到了其他事务未提交的数据 举个例子 有一个table表 如果执行顺序如下 这种情况下左边查询的结果会是101 正是因为读取到了另一个事务未提交的数据 幻读 在一个事务中
  • selenium 获取cookie 并使用

    selenium 获取cookie 参数设置 以获取阿里云cookie范例 from selenium import webdriver import json url https account aliyun com login logi
  • 使用Python的方式理解Golang的结构体struct

    Go源码 package GoTools import fmt 定义结构体存储密码 type Config struct password string func InitConfig password string Config c ne
  • Vue用户进行页面切换(路由跳转)时,动态改变路由的动画(transition效果)

    当我们在使用Vue Router时 为了用户有更好的视觉效果及体验 我们通常需要实现基于路由的动态过渡效果 github https github com Rise Devin FullStack Product Transport Use
  • retinaface代码讲解_「干货」RetinaFace最强开源人脸识别算法

    看来最早商业化的人脸检测为目标检测算法 依然是各大CV方向AI公司的必争之地 那我们今天主角就是RetinaFace RetinaFace 是今年5月份出现的人脸检测算法 当时取得了state of the art 作者也开源了代码 过去了
  • 集合的知识

    集合 collection集合的常用方法 collection的特点 Collection代表单列集合 每个元素 数据 只包含一个值 Map代表双列集合 每个元素包含两个值 键值对 Collection集合特点 由于collection是一
  • gRpc指南

    本文翻译自官网 原文 https grpc io docs languages java quickstart 快速开始 下面通过一个简单的样例 让你快速上手基于java的gRpc的使用 前置条件 JDK7以上版本 获取示例代码 示例代码是