{"id":164,"date":"2021-05-11T09:01:01","date_gmt":"2021-05-11T01:01:01","guid":{"rendered":"https:\/\/blog.king011.com\/?p=164"},"modified":"2025-02-13T10:06:43","modified_gmt":"2025-02-13T02:06:43","slug":"grpc-%e5%92%8c-grpc-gateway-%e5%be%a9%e7%94%a8%e5%90%8c%e4%b8%80%e7%ab%af%e5%8f%a3%e7%9a%84%e6%ad%a3%e7%a2%ba%e5%a7%bf%e5%8b%a2","status":"publish","type":"post","link":"https:\/\/blog.king011.com\/?p=164","title":{"rendered":"grpc \u548c grpc-gateway \u5fa9\u7528\u540c\u4e00\u7aef\u53e3\u7684\u6b63\u78ba\u59ff\u52e2"},"content":{"rendered":"\n<p>\u7db2\u4e0a\u6709\u5f88\u591a\u8a0e\u8ad6 grpc \u548c grpc-gateway \u5fa9\u7528\u540c\u4e00\u7aef\u53e3\u7684\u6587\u7ae0\uff0c\u7136\u57fa\u672c\u90fd\u662f\u4e92\u76f8 copy \u901a\u5e38\u4e3b\u8981\u5c31\u662f\u544a\u8a34\u4f60 \u5728 ServeHTTP \u4e2d\u5224\u65b7\u4e0b\u5982\u679c\u662f grpc \u8acb\u6c42\u5c31\u4ea4\u7d66 grpc \u670d\u52d9\u8655\u7406 \uff0c\u5426\u5247\u4ea4\u7d66 gateway \u9032\u884c\u8655\u7406\uff0c\u4ee3\u78bc\u985e\u4f3c\u9019\u6a23\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tcontextType := r.Header.Get(`Content-Type`)\n\tif r.ProtoMajor == 2 &amp;&amp; strings.Contains(contextType, `application\/grpc`) {\n\t\ts.gtcp.ServeHTTP(w, r) \/\/ application\/grpc \u8def\u7531\u7d66 grpc\n\t} else {\n\t\ts.proxyMux.ServeHTTP(w, r) \/\/ \u975e grpc \u8def\u7531\u7d66 gateway\n\t}\n}<\/code><\/pre>\n\n\n\n<p>\u4e0a\u8ff0\u4ee3\u78bc\u53ef\u4ee5\u5de5\u4f5c\uff0c\u4f46\u9019\u662f\u4e0d\u5920\u7684\uff0c\u8981\u6b63\u5e38\u7528\u65bc\u751f\u7522\u74b0\u5883\uff0c\u81f3\u5c11\u9084\u6709\u5169\u500b\u554f\u984c\u9700\u8981\u8a73\u7d30\u8a0e\u8ad6<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>gateway \u548c grpc \u5176\u5be6\u53ef\u4ee5\u4e0d\u901a\u904e socket \u800c\u76f4\u63a5 memory copy \u901a\u8a0a<\/li>\n\n\n\n<li>h2c \u7684\u652f\u6301<\/li>\n<\/ul>\n\n\n\n<p>\u672c\u6587\u4e3b\u8981\u8a0e\u8ad6\u4e0a\u8ff0\u5169\u500b\u554f\u984c\uff0c\u672c\u6587\u5047\u8a2d\u4f60\u5df2\u7d93\u719f\u6089\u4e86 golang grpc grpc-gateway \u7b49\u6280\u8853\uff0c\u4e0d\u6703\u5c0d\u9019\u4e9b\u57fa\u790e\u6280\u8853\u9032\u884c\u8a73\u7d30\u8aaa\u660e\uff0c\u7562\u7adf\u672c\u6587\u8a0e\u8ad6\u7684\u662f\u5c0d\u9019\u4e9b\u6280\u8853\u719f\u6089\u5f8c\u7684\u6280\u5de7\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u7aef\u53e3\u5fa9\u7528<\/h2>\n\n\n\n<p>\u9996\u5148\u662f\u7aef\u53e3\u5fa9\u7528\u5176\u5b83\u6587\u7ae0\u5df2\u7d93\u6709\u8a73\u7d30\u8aaa\u660e\u672c\u6587\u9084\u662f\u7c97\u7565\u8b1b\u4e0b\uff0c\u56e0\u7232 gateway \u5c0d\u5916\u63d0\u4f9b\u7684\u662f http1.1\u517c\u5bb9\u63a5\u53e3\uff0c\u540c\u6642 grpc \u4f7f\u7528 http2\uff0chttp2 \u53c8\u517c\u5bb9 http1.1 \u6240\u4ee5\u53ea\u9700\u8abf\u7528 net.Listen \u76e3\u807d\u4e00\u500b\u7aef\u53e3\uff0c\u7136\u5f8c\u5728 ServeHTTP \u4e2d\u5224\u65b7\u4e0b\u9023\u63a5\u5354\u8b70\u5c07\u5176\u5206\u6d41\u5373\u53ef\uff0c\u4ee3\u78bc\u898b\u6587\u7ae0\u958b\u982d\u7684\u793a\u4f8b\u3002<\/p>\n\n\n\n<p>\u6b64\u8655\u5176\u5b83\u6587\u7ae0\u4e2d gateway \u6703\u901a\u904e socket \u9023\u63a5\u672c\u5730\u7684 grpc \u9032\u884c\u4e2d\u8f49\uff0c\u7136\u9019\u5176\u5be6\u61c9\u8a72\u512a\u5316\u4e00\u4e0b\uff0c\u56e0\u7232\u5982\u679c\u901a\u904e socket \u901a\u8a0a\uff0c\u6578\u64da\u5fc5\u9808\u63d0\u4ea4\u7d66 os\uff0cos \u63d0\u4ea4\u7d66\u7db2\u5361\uff0c\u9019\u7121\u7591\u589e\u52a0\u4e86\u4e0d\u5c0f\u7684\u984d\u5916\u958b\u92b7\uff0c\u4f46 gateway \u548c grpc \u670d\u52d9\u5176\u5be6\u5de5\u4f5c\u5728\u540c\u4e00\u7a0b\u5f0f\u4e2d\u6240\u4ee5\u5b8c\u5168\u53ef\u4ee5\u901a\u904e memory copy \u4f86\u901a\u8a0a\u3002\u4e26\u4e14\u5373\u4f7f\u5c0d\u5916\u63d0\u4f9b h2 \u7684 grpc \u670d\u52d9\uff0cgateway\u4e5f\u53ef\u4ee5\u4f7f\u7528 h2c \u548c grpc \u901a\u8a0a\u4e5f\u7bc0\u7701\u4e86\u4e00\u9ede\u52a0\u5bc6\u89e3\u5bc6\u7684\u958b\u92b7\u3002<\/p>\n\n\n\n<p>\u8981\u5be6\u73fe memory copy \u901a\u8a0a\u4e5f\u5f88\u7c21\u55ae\uff0c\u5c07 memory \u5be6\u73fe net.Conn \u8207 net.Listener \u63a5\u53e3\u5373\u53ef\uff0c\u5f97\u76ca\u8207 golang \u5f37\u5927\u7684\u63a5\u53e3\uff0cgateway \u548c grpc \u4ee3\u78bc\u5176\u5be6\u662f\u5de5\u4f5c\u5728 net.Conn \u548c net.Listener \u63a5\u53e3\u4e0a\u800c\u975e\u5de5\u4f5c\u5728 tcp \u76f8\u95dc\u4ee3\u78bc\u4e0a\u3002\u672c\u55b5\u4e0a\u7bc7\u6587\u7ae0\u5df2\u7d93\u8a73\u7d30\u8a0e\u8ad6\u4e86\u5982\u4f55\u4ee5 memory copy \u4f86\u5be6\u73fe \u9019\u5169\u500b\u63a5\u53e3\uff0c\u6b64\u8655\u4e0d\u518d\u8a0e\u8ad6\uff0c\u8acb\u770b\u4e0a\u7bc7\u6587\u7ae0<a href=\"?p=140\">\u300anet.Listener \u6280\u5de7 net.Pipe \u7684\u5999\u7528\u300b<\/a>\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">h2c \u7684\u652f\u6301<\/h2>\n\n\n\n<p>\u8981\u652f\u6301 h2c \u4e0d\u80fd\u4f7f\u7528\u6a19\u6e96\u5eab\u7684 http.Serve \u5426\u5247 grpc \u5ba2\u6236\u7aef\u5c07\u7121\u6cd5\u6b63\u5e38\u5de5\u4f5c\uff0c\u9019\u662f\u56e0\u7232\u6a19\u6e96\u5eab\u4e26\u6c92\u6709\u652f\u6301 h2c \u5354\u8b70\uff0c\u89e3\u6c7a\u4e5f\u5f88\u5bb9\u6613 x \u5eab\u5df2\u7d93\u63d0\u4f9b\u4e86\u5c0d h2c \u7684\u652f\u6301\uff0c\u4f7f\u7528 h2c \u6642\u8abf\u7528 x \u5eab \u63d0\u4f9b\u7684 h2c \u5373\u53ef\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u793a\u4f8b\u4ee3\u78bc<\/h2>\n\n\n\n<p>\u6709\u4e86\u4e0a\u8ff0\u7406\u8ad6\u4e0b\u9762\u4f86\u4e00\u6b65\u6b65\u7684\u5be6\u73fe\u500b\u793a\u4f8b\u4ee3\u78bc\u3002<\/p>\n\n\n\n<p>\u9996\u5148\u5b9a\u7fa9\u4e00\u7d44 grpc \u63a5\u53e3\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>syntax = \"proto3\";\n\npackage math;\noption go_package = \"test\/grpc\/math\";\n\nimport \"google\/api\/annotations.proto\";\n\nservice Cat {\n    rpc Sum (SumRequest) returns (SumResponse){\n        option (google.api.http) = {\n            post: \"\/api\/v1\/math\/sum\"\n            body: \"*\"\n        };\n    }\n    rpc Version (VersionRequest) returns (VersionResponse){\n        option (google.api.http) = {\n            get: \"\/api\/v1\/math\/version\"\n        };\n    }\n}\n\nmessage SumRequest{\n    repeated int32 vals = 1;\n}\nmessage SumResponse{\n    int32 val = 1;\n}\nmessage VersionRequest{\n}\nmessage VersionResponse{\n    string val = 1;\n}<\/code><\/pre>\n\n\n\n<p>\u7136\u5f8c\u5275\u5efa\u4e00\u500b math.go \u5728\u88cf\u9762\u5be6\u73fe\u670d\u52d9\u5668\uff0c\u4e26\u4e14\u63d0\u4f9b\u4e00\u500b newGRPCServer \u51fd\u6578\u7528\u65bc\u5275\u5efa grpc \u670d\u52d9\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package main\n\nimport (\n\t\"context\"\n\tgrpc_math \"test\/grpc\/protocol\/math\"\n\n\t\"github.com\/grpc-ecosystem\/grpc-gateway\/v2\/runtime\"\n\t\"google.golang.org\/grpc\"\n)\n\nfunc newGRPCServer(mux *runtime.ServeMux, cc *grpc.ClientConn) *grpc.Server {\n\ts := grpc.NewServer()\n\n\t\/\/ register to grpc\n\tgrpc_math.RegisterCatServer(s, Math{})\n\tif mux != nil &amp;&amp; cc != nil {\n\t\t\/\/ register to gateway\n\t\tgrpc_math.RegisterCatHandler(context.Background(), mux, cc)\n\t}\n\treturn s\n}\n\ntype Math struct {\n\tgrpc_math.UnimplementedCatServer\n}\n\nfunc (Math) Sum(ctx context.Context, req *grpc_math.SumRequest) (*grpc_math.SumResponse, error) {\n\tvar sum int32 = 0\n\tfor _, val := range req.Vals {\n\t\tsum += val\n\t}\n\treturn &amp;grpc_math.SumResponse{\n\t\tVal: sum,\n\t}, nil\n}\nfunc (c Math) Version(context.Context, *grpc_math.VersionRequest) (*grpc_math.VersionResponse, error) {\n\treturn &amp;grpc_math.VersionResponse{\n\t\tVal: `v1.0.0`,\n\t}, nil\n}<\/code><\/pre>\n\n\n\n<p>\u7136\u5f8c\u5275\u5efa\u4e00\u500b Server \u4f86\u5be6\u73fe\u4e0a\u8ff0\u7406\u8ad6\uff0c\u6211\u5011\u9996\u5148\u8003\u616e Server \u9700\u8981\u7684\u5c6c\u6027\u5305\u62ec<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>PipeListener \u8207 grpc.Server  \u7528\u65bc\u63d0\u4f9b momory copy \u7684 grpc \u670d\u52d9<\/li>\n\n\n\n<li>net.Listener \u8207 grpc.Server \u7528\u65bc\u63d0\u4f9b \u5c0d\u5916\u7684 grpc \u670d\u52d9<\/li>\n\n\n\n<li>gateway \u7684 runtime.ServeMux \u7528\u65bc\u5c07 grpc \u5305\u88dd\u7232 http \u5c0d\u5916\u63d0\u4f9b\u670d\u52d9<\/li>\n<\/ul>\n\n\n\n<p>\u5176\u5b9a\u7fa9\u5982\u4e0b\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>type Server struct {\n\tpipe  *PipeListener\n\tgpipe *grpc.Server\n\n\ttcp  net.Listener\n\tgtcp *grpc.Server\n\n\tproxyMux *runtime.ServeMux\n}\n\nfunc NewServer(addr string) (s *Server, e error) {\n\ttcp, e := net.Listen(`tcp`, addr)\n\tif e != nil {\n\t\treturn\n\t}\n\n\tpipe := ListenPipe()\n\tclientConn, e := grpc.Dial(`pipe`,\n\t\tgrpc.WithInsecure(),\n\t\tgrpc.WithContextDialer(func(c context.Context, s string) (net.Conn, error) {\n\t\t\treturn pipe.DialContext(c, `pipe`, s)\n\t\t}),\n\t)\n\tif e != nil {\n\t\treturn\n\t}\n\tproxyMux := runtime.NewServeMux()\n\n\ts = &amp;Server{\n\t\tpipe:     pipe,\n\t\ttcp:      tcp,\n\t\tgpipe:    newGRPCServer(proxyMux, clientConn),\n\t\tgtcp:     newGRPCServer(nil, nil),\n\t\tproxyMux: proxyMux,\n\t}\n\treturn\n}<\/code><\/pre>\n\n\n\n<p>\u6ce8\u610f\u4e0a\u8ff0 grpc.Dial \u8abf\u7528\uff0c\u4f7f\u7528 grpc.WithContextDialer \u7528 PipeDialer \u66ff\u4ee3\u4e86\u9ed8\u8a8d\u7684 tcpDialer \u4f86\u5be6\u73fe memory copy \u901a\u8a0a\u3002<\/p>\n\n\n\n<p>\u4e4b\u5f8c\u5982\u6587\u7ae0\u958b\u982d\u7684\u793a\u4f8b\u5728 ServeHTTP \u4e2d\u5206\u6d41\u5373\u53ef\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tcontextType := r.Header.Get(`Content-Type`)\n\tif r.ProtoMajor == 2 &amp;&amp; strings.Contains(contextType, `application\/grpc`) {\n\t\ts.gtcp.ServeHTTP(w, r) \/\/ application\/grpc \u8def\u7531\u7d66 grpc\n\t} else {\n\t\ts.proxyMux.ServeHTTP(w, r) \/\/ \u975e grpc \u8def\u7531\u7d66 gateway\n\t}\n}<\/code><\/pre>\n\n\n\n<p>\u5728 h2c \u6642\u9700\u8981\u8abf\u7528 x \u5eab\u652f\u6301 h2c\uff0ch2 \u53ef\u4ee5\u76f4\u63a5\u8abf\u7528\u6a19\u6e96\u5eab\u5373\u53ef\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>func (s *Server) Serve() (e error) {\n\tgo s.gpipe.Serve(s.pipe)\n\n\t\/\/ \u914d\u7f6e h2c\n\tvar httpServer http.Server\n\tvar http2Server http2.Server\n\te = http2.ConfigureServer(&amp;httpServer, &amp;http2Server)\n\tif e != nil {\n\t\treturn\n\t}\n\thttpServer.Handler = h2c.NewHandler(s, &amp;http2Server)\n\t\/\/ http.Serve \u4e0d\u652f\u6301 h2c\n\t\/\/ \u5982\u679c\u76f4\u63a5\u4f7f\u7528 http.Serve \u5c07\u4f7f\u7528 grpc \u5ba2\u6236\u7aef \u7121\u6cd5\u6b63\u5e38\u8a2a\u554f\n\te = httpServer.Serve(s.tcp)\n\treturn\n}\nfunc (s *Server) ServeTLS(certFile, keyFile string) (e error) {\n\tgo s.gpipe.Serve(s.pipe)\n\n\te = http.ServeTLS(s.tcp, s, certFile, keyFile)\n\treturn\n}<\/code><\/pre>\n\n\n\n<div class=\"wp-block-file\"><a href=\"https:\/\/blog.king011.com\/wp-content\/uploads\/2021\/05\/grpc.7z\">\u5b8c\u6574\u793a\u4f8b\u4e0b\u8f09<\/a><\/div>\n","protected":false},"excerpt":{"rendered":"<p>grpc \u548c grpc-gateway \u5fa9\u7528\u540c\u4e00\u7aef\u53e3\u7684\u65b9\u6848<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[11,30,2],"tags":[12,31,32,33],"class_list":["post-164","post","type-post","status-publish","format-standard","hentry","category-golang","category-grpc","category-code","tag-golang","tag-grpc","tag-grpc-gateway","tag-rpc"],"blocksy_meta":{"styles_descriptor":{"styles":{"desktop":"","tablet":"","mobile":""},"google_fonts":[],"version":6}},"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/blog.king011.com\/index.php?rest_route=\/wp\/v2\/posts\/164","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.king011.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.king011.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.king011.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.king011.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=164"}],"version-history":[{"count":15,"href":"https:\/\/blog.king011.com\/index.php?rest_route=\/wp\/v2\/posts\/164\/revisions"}],"predecessor-version":[{"id":365,"href":"https:\/\/blog.king011.com\/index.php?rest_route=\/wp\/v2\/posts\/164\/revisions\/365"}],"wp:attachment":[{"href":"https:\/\/blog.king011.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=164"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.king011.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=164"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.king011.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=164"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}