Connect协议的详细指南

764 阅读3分钟

连接

BuildReport CardGoDoc

Connect是一个纤细的库,用于构建与浏览器和gRPC兼容的HTTP APIs。你可以写一个简短的协议缓冲区模式并实现你的应用逻辑,Connect会生成代码来处理数据采集、路由、压缩和内容类型协商。它还会生成一个习惯性的、类型安全的客户端。处理程序和客户端支持三种协议:gRPC、gRPC-Web和Connect自己的协议。

Connect协议是一个简单的、仅有POST的协议,可以通过HTTP/1.1或HTTP/2工作。它吸收了gRPC和gRPC-Web的最佳部分,包括流媒体,并将它们打包成一个协议,在浏览器、单体和微服务中同样适用。调用Connect API就像使用curl 一样简单。用我们的现场演示试试吧。

curl \
    --header "Content-Type: application/json" \
    --data '{"sentence": "I feel happy."}' \
    https://demo.connect.build/buf.connect.demo.eliza.v1.ElizaService/Say

处理程序和客户端也支持gRPC和gRPC-Web协议,包括流媒体、报头、拖车和错误细节。gRPC兼容的服务器反射健康检查可以作为独立的软件包提供。我们可以用grpcurl ,而不是cURL,来调用我们的API。

go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
grpcurl \
    -d '{"sentence": "I feel happy."}' \
    demo.connect.build:443 \
    buf.connect.demo.eliza.v1.ElizaService/Say

在引擎盖下,Connect只是协议缓冲区和标准库:没有自定义的HTTP实现,没有新的名称解析或负载平衡API,也没有任何惊喜。你已经知道的关于net/http 的一切仍然适用,任何与http.Serverhttp.Client 、或http.Handler 一起工作的包也可以与Connect一起工作。

关于Connect的更多信息,请参见公告博文connect.build的文档(尤其是Go的入门指南)、演示服务协议规范

一个小例子

很好奇这一切在实践中是什么样子的?从Protobuf模式中,我们生成一个小型的RPC包。使用该包,我们可以建立一个服务器:

package main

import (
  "context"
  "log"
  "net/http"

  "github.com/bufbuild/connect-go"
  pingv1 "github.com/bufbuild/connect-go/internal/gen/connect/ping/v1"
  "github.com/bufbuild/connect-go/internal/gen/connect/ping/v1/pingv1connect"
  "golang.org/x/net/http2"
  "golang.org/x/net/http2/h2c"
)

type PingServer struct {
  pingv1connect.UnimplementedPingServiceHandler // returns errors from all methods
}

func (ps *PingServer) Ping(
  ctx context.Context,
  req *connect.Request[pingv1.PingRequest],
) (*connect.Response[pingv1.PingResponse], error) {
  // connect.Request and connect.Response give you direct access to headers and
  // trailers. No context-based nonsense!
  log.Println(req.Header().Get("Some-Header"))
  res := connect.NewResponse(&pingv1.PingResponse{
    // req.Msg is a strongly-typed *pingv1.PingRequest, so we can access its
    // fields without type assertions.
    Number: req.Msg.Number,
  })
  res.Header().Set("Some-Other-Header", "hello!")
  return res, nil
}

func main() {
  mux := http.NewServeMux()
  // The generated constructors return a path and a plain net/http
  // handler.
  mux.Handle(pingv1connect.NewPingServiceHandler(&PingServer{}))
  err := http.ListenAndServe(
    "localhost:8080",
    // For gRPC clients, it's convenient to support HTTP/2 without TLS. You can
    // avoid x/net/http2 by using http.ListenAndServeTLS.
    h2c.NewHandler(mux, &http2.Server{}),
  )
  log.Fatalf("listen failed: %v", err)
}

在服务器运行时,你可以用任何gRPC或Connect客户端发出请求。使用connect-go 来编写一个客户端:

package main

import (
  "context"
  "log"
  "net/http"

  "github.com/bufbuild/connect-go"
  pingv1 "github.com/bufbuild/connect-go/internal/gen/connect/ping/v1"
  "github.com/bufbuild/connect-go/internal/gen/connect/ping/v1/pingv1connect"
)

func main() {
  client := pingv1connect.NewPingServiceClient(
    http.DefaultClient,
    "http://localhost:8080/",
  )
  req := connect.NewRequest(&pingv1.PingRequest{
    Number: 42,
  })
  req.Header().Set("Some-Header", "hello from connect")
  res, err := client.Ping(context.Background(), req)
  if err != nil {
    log.Fatalln(err)
  }
  log.Println(res.Msg)
  log.Println(res.Header().Get("Some-Other-Header"))
}

当然,http.ListenAndServehttp.DefaultClient 并不适合生产使用!请参阅Connect的部署文档,了解配置超时、连接池、可观察性和h2c的指南。

生态系统

状态

这个模块是一个测试版:我们在生产中依靠它,但我们可能会在收集早期采用者的反馈时做一些改变。我们计划在10月发布稳定版,即Go 1.19发布后不久。

支持和版本划分

connect-go 支持:

在这些参数范围内,Connect遵循语义上的版本划分。