【发布时间】:2018-08-20 23:50:07
【问题描述】:
我正在尝试将 GoLang“Go”与 gRPC 一起理解,并使简单的服务具有可扩展性。
假设我有一个调用 service1(添加数字)的 client1(添加数字)调用 service2(确定结果是否为素数),并且 service2 将结果返回给 service1,service1 通过 gRPC 将结果返回给 client1。
当我使用协议缓冲区“proto3”并通过 protoc 生成 Go 代码时。 我得到了以一种特定方式调用服务的生成方法。 我认为异步调用方法“Go”没有区别。
底层调用似乎是“Invoke”,我相信它是同步的,一旦收到结果,调用就会返回。
如何使 service1 “高性能”,我知道我可以在集群中运行它并拥有副本,但这意味着我只能根据集群中的实例数量为客户端提供服务。
我希望“单一”服务能够为多个客户提供服务(例如 1000 个)。
这是一个简单的服务器,我不确定这是否有效:
我知道getprime 函数每次都会拨号,
并且这可能会被移动以使这个表盘持续存在并被重新使用;但更重要的是,我想做一个简单的高性能可扩展服务并获得一个很好的理解。
(A) 也许整个设计不正确, service1 应该只是返回 收到“ack”指令后,立即进行加法并将下一个请求发送到 sercice2,确定答案是否为素数;再次 service2 只是响应收到请求的确认。一旦服务确定了素数,就会向客户端发出呼叫并给出答案。
如果上面的(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)
}
}
【问题讨论】:
-
Go 通常不需要异步 API,您可以使用 goroutine 管理并发。
-
或者,如果您询问如何处理并发请求,这就是
grpc.Server所做的。除非您自己实施,否则您无需担心。 -
谢谢吉姆;那么回调呢,gRPC 调用你的 gRPC 方法实现来执行一些操作。在我的示例中,它是计算方法。如果要执行长时间运行的操作,您是否需要在此方法中使用通道和执行例程?
-
恐怕我还是不明白。 Go grpc API 不是围绕回调构建的。如果要进行长时间运行的操作,请进行长时间运行的操作。通道和 goroutine 只是语言的结构——这有点像问你是否会使用函数和变量来执行长时间运行的操作。
-
如果你问 Go gRPC 服务器是否会处理并发连接;是的,它会的,如果没有,它就不会很好地工作。