去gRPC简单服务异步和同步解释

时间:2018-03-12 20:38:01

标签: go grpc

我试图与gRPC一起理解GoLang“Go”并使一个简单的服务可扩展。

假设我有一个client1调用service1(添加数字)调用service2(确定结果是否为prime),service2将结果返回给service1,该结果通过gRPC将结果返回给client1。

当我使用协议缓冲区“proto3”并通过protoc生成Go代码时。 我得到生成的方法,以一种特定的方式调用服务。 我认为没有任何区别可以异步调用方法“Go”。

基础调用似乎是“调用”,我认为它是同步的,一旦收到结果,调用就会返回。

如何使service1“performant”,我知道我可以在群集中运行它并拥有副本,但这意味着我只能按照群集中的实例数量为客户端提供服务。

我希望“单一”服务能够为多个客户端提供服务(例如1000)。

这是一个简单的服务器,我不确定这是否有效: 我知道getprime函数每次都会拨号, 这可能会被移动,使这个表盘保持不变并重新使用;但更重要的是,我希望制作一个简单的高性能可扩展服务并获得良好的理解。

(A) 也许整个设计是不正确的,service1应该返回 一旦收到指令“ack”,就做加法并向sercice2发送下一个请求,确定答案是否为素数;再次,service2只响应对收到的请求的确认。一旦由服务2确定了素数,就会给答案客户打电话。

如果上述(A)是更好的方法,那么请解释下面的瓶颈;处理多个客户端时会发生什么? 对“Listen”的调用会做什么,“阻止或不阻止”等等。

package main

import (
    pb "demo/internal/pkg/proto_gen/calc"
    "fmt"
    "golang.org/x/net/context"
    "google.golang.org/grpc"
    "google.golang.org/grpc/reflection"
    "log"
    "net"
)

const (
    port = ":8080"
)

type service struct {
}

func (s *service) Calculate(ctx context.Context, req *pb.Instruction) (*pb.Response, error) {

    var answer float64
    answer = req.Number1 + req.Number2

    // call service prime
    p := getprime(int(answer))
    pa := pb.PrimeAnswer{Prime: p}
    return &pb.Response{Answer: answer, Prime: &pa}, nil
}

const (
    primeAddress = "127.0.0.1:8089"
)

func getprime(number int) bool {
    conn, err := grpc.Dial(primeAddress, grpc.WithInsecure())
    if err != nil {
        log.Fatalf("Did not connect to prime service: %v", err)
    }
    defer conn.Close()

    client := pb.NewPrimeServiceClient(conn)
    p := pb.PrimeMessage{"", float64(number)}

    r, err := client.Prime(context.Background(), &p)
    if err != nil {
        log.Fatalf("Call to prime service failed: %v", err)
    }
    return r.Prime
}

func main() {
    lis, err := net.Listen("tcp", port)
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }

    s := grpc.NewServer()
    pb.RegisterCalculatorServer(s, &service{})
    reflection.Register(s)
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

2 个答案:

答案 0 :(得分:2)

感谢您的提问。确实,gRPC-Go只是同步;那就是你的Unary RPC(你的例子中的那个)只有在RPC完成后才会返回(从服务器得到响应)。

关于表现:

  1. 拨号操作建立了一个可能很昂贵的底层连接。因此,每次调用getprime时都不明智。更好的方法是创建一个客户端,保持它并调用它上面的主服务器。这样只有第一个RPC会产生连接成本。
  2. 对于每个RPC请求,服务器都会启动一个goroutine来处理该请求。所以总的来说,这应该可以很好地扩展。
  3. 关于(A):服务处理程序对另一台服务器进行RPC调用并在返回之前等待其响应的情况并不少见。 请注意,服务器无法调用客户端。

答案 1 :(得分:1)

说出JimB所说的答案:" Synchronous"表示进行远程调用的函数在继续之前等待回复,而不是整个服务器或客户端进行回复。即使处理同步调用,服务器通常也是多线程的;当它响应第一个电话时,它可以接受并接听第二个电话。

同样,如果客户端有多个并发任务,每个任务都运行gRPC调用,则不会阻止该进程。像这样的客户端可能包括为最终用户提供服务的net/http服务器,或者处理多个RPC的gRPC服务器。

如果您想要从进行RPC调用的特定函数执行其他操作,可能添加显式go语句的位置。例如,如果要同时发出多个RPC调用然后等待所有结果进入,则可以在examples of fan-out calls之后编写代码。