20分钟
4.5.4 和Web服务共存
gRPC构建在HTTP/2协议之上,因此我们可以将gRPC服务和普通的Web服务架设在同一个端口之上。
对于没有启动TLS协议的服务则需要对HTTP2/2特性做适当的调整:
func main() {
mux := http.NewServeMux()
h2Handler := h2c.NewHandler(mux, &http2.Server{})
server = &http.Server{Addr: ":3999", Handler: h2Handler}
server.ListenAndServe()
}启用普通的https服务器则非常简单:
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintln(w, "hello")
})
http.ListenAndServeTLS(port, "server.crt", "server.key",
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
mux.ServeHTTP(w, r)
return
}),
)
}而单独启用带证书的gRPC服务也是同样的简单:
func main() {
creds, err := credentials.NewServerTLSFromFile("server.crt", "server.key")
if err != nil {
log.Fatal(err)
}
grpcServer := grpc.NewServer(grpc.Creds(creds))
...
}因为gRPC服务已经实现了ServeHTTP方法,可以直接作为Web路由处理对象。如果将gRPC和Web服务放在一起,会导致gRPC和Web路径的冲突,在处理时我们需要区分两类服务。
我们可以通过以下方式生成同时支持Web和gRPC协议的路由处理函数:
func main() {
...
http.ListenAndServeTLS(port, "server.crt", "server.key",
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.ProtoMajor != 2 {
mux.ServeHTTP(w, r)
return
}
if strings.Contains(
r.Header.Get("Content-Type"), "application/grpc",
) {
grpcServer.ServeHTTP(w, r) // gRPC Server
return
}
mux.ServeHTTP(w, r)
return
}),
)
}首先gRPC是建立在HTTP/2版本之上,如果HTTP不是HTTP/2协议则必然无法提供gRPC支持。同时,每个gRPC调用请求的Content-Type类型会被标注为"application/grpc"类型。
这样我们就可以在gRPC端口上同时提供Web服务了。
学员评价