最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go?gRPC進(jìn)階教程gRPC轉(zhuǎn)換HTTP

 更新時(shí)間:2022年06月16日 11:19:14   作者:煙花易冷人憔悴  
這篇文章主要為大家介紹了Go?gRPC進(jìn)階教程gRPC轉(zhuǎn)換HTTP教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

我們通常把RPC用作內(nèi)部通信,而使用Restful Api進(jìn)行外部通信。為了避免寫(xiě)兩套應(yīng)用,我們使用grpc-gateway把gRPC轉(zhuǎn)成HTTP。服務(wù)接收到HTTP請(qǐng)求后,grpc-gateway把它轉(zhuǎn)成gRPC進(jìn)行處理,然后以JSON形式返回?cái)?shù)據(jù)。本篇代碼以上篇為基礎(chǔ),最終轉(zhuǎn)成的Restful Api支持bearer token驗(yàn)證、數(shù)據(jù)驗(yàn)證,并添加swagger文檔。

gRPC轉(zhuǎn)成HTTP

編寫(xiě)和編譯proto

1.編寫(xiě)simple.proto

syntax = "proto3";
package proto;
import "github.com/mwitkow/go-proto-validators/validator.proto";
import "go-grpc-example/10-grpc-gateway/proto/google/api/annotations.proto";
message InnerMessage {
  // some_integer can only be in range (1, 100).
  int32 some_integer = 1 [(validator.field) = {int_gt: 0, int_lt: 100}];
  // some_float can only be in range (0;1).
  double some_float = 2 [(validator.field) = {float_gte: 0, float_lte: 1}];
}
message OuterMessage {
  // important_string must be a lowercase alpha-numeric of 5 to 30 characters (RE2 syntax).
  string important_string = 1 [(validator.field) = {regex: "^[a-z]{2,5}$"}];
  // proto3 doesn't have `required`, the `msg_exist` enforces presence of InnerMessage.
  InnerMessage inner = 2 [(validator.field) = {msg_exists : true}];
}
service Simple{
  rpc Route (InnerMessage) returns (OuterMessage){
      option (google.api.http) ={
          post:"/v1/example/route"
          body:"*"
      };
  }
}

可以看到,proto變化不大,只是添加了API的路由路徑

      option (google.api.http) ={
          post:"/v1/example/route"
          body:"*"
      };

2.編譯simple.proto

simple.proto文件引用了google/api/annotations.proto,先要把它編譯了。我這里是把google/文件夾直接復(fù)制到項(xiàng)目中的proto/目錄中進(jìn)行編譯。發(fā)現(xiàn)annotations.proto引用了google/api/http.proto,那把它也編譯了。

進(jìn)入annotations.proto所在目錄,編譯:

protoc --go_out=plugins=grpc:./ ./http.proto
protoc --go_out=plugins=grpc:./ ./annotations.proto

進(jìn)入simple.proto所在目錄,編譯:

#生成simple.validator.pb.go和simple.pb.go
protoc --govalidators_out=. --go_out=plugins=grpc:./ ./simple.proto
#生成simple.pb.gw.go
protoc --grpc-gateway_out=logtostderr=true:./ ./simple.proto

以上完成proto編譯,接著修改服務(wù)端代碼。

服務(wù)端代碼修改

1.server/文件夾下新建gateway/目錄,然后在里面新建gateway.go文件

package gateway
import (
	"context"
	"crypto/tls"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
	pb "go-grpc-example/10-grpc-gateway/proto"
	"go-grpc-example/10-grpc-gateway/server/swagger"
	"github.com/grpc-ecosystem/grpc-gateway/runtime"
	"golang.org/x/net/http2"
	"golang.org/x/net/http2/h2c"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"
)
// ProvideHTTP 把gRPC服務(wù)轉(zhuǎn)成HTTP服務(wù),讓gRPC同時(shí)支持HTTP
func ProvideHTTP(endpoint string, grpcServer *grpc.Server) *http.Server {
	ctx := context.Background()
	//獲取證書(shū)
	creds, err := credentials.NewClientTLSFromFile("../tls/server.pem", "go-grpc-example")
	if err != nil {
		log.Fatalf("Failed to create TLS credentials %v", err)
	}
	//添加證書(shū)
	dopts := []grpc.DialOption{grpc.WithTransportCredentials(creds)}
	//新建gwmux,它是grpc-gateway的請(qǐng)求復(fù)用器。它將http請(qǐng)求與模式匹配,并調(diào)用相應(yīng)的處理程序。
	gwmux := runtime.NewServeMux()
	//將服務(wù)的http處理程序注冊(cè)到gwmux。處理程序通過(guò)endpoint轉(zhuǎn)發(fā)請(qǐng)求到grpc端點(diǎn)
	err = pb.RegisterSimpleHandlerFromEndpoint(ctx, gwmux, endpoint, dopts)
	if err != nil {
		log.Fatalf("Register Endpoint err: %v", err)
	}
	//新建mux,它是http的請(qǐng)求復(fù)用器
	mux := http.NewServeMux()
	//注冊(cè)gwmux
	mux.Handle("/", gwmux)
	log.Println(endpoint + " HTTP.Listing whth TLS and token...")
	return &http.Server{
		Addr:      endpoint,
		Handler:   grpcHandlerFunc(grpcServer, mux),
		TLSConfig: getTLSConfig(),
	}
}
// grpcHandlerFunc 根據(jù)不同的請(qǐng)求重定向到指定的Handler處理
func grpcHandlerFunc(grpcServer *grpc.Server, otherHandler http.Handler) http.Handler {
	return h2c.NewHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
			grpcServer.ServeHTTP(w, r)
		} else {
			otherHandler.ServeHTTP(w, r)
		}
	}), &http2.Server{})
}
// getTLSConfig獲取TLS配置
func getTLSConfig() *tls.Config {
	cert, _ := ioutil.ReadFile("../tls/server.pem")
	key, _ := ioutil.ReadFile("../tls/server.key")
	var demoKeyPair *tls.Certificate
	pair, err := tls.X509KeyPair(cert, key)
	if err != nil {
		grpclog.Fatalf("TLS KeyPair err: %v\n", err)
	}
	demoKeyPair = &pair
	return &tls.Config{
		Certificates: []tls.Certificate{*demoKeyPair},
		NextProtos:   []string{http2.NextProtoTLS}, // HTTP2 TLS支持
	}
}

它主要作用是把不用的請(qǐng)求重定向到指定的服務(wù)處理,從而實(shí)現(xiàn)把HTTP請(qǐng)求轉(zhuǎn)到gRPC服務(wù)。

2.gRPC支持HTTP

    //使用gateway把grpcServer轉(zhuǎn)成httpServer
	httpServer := gateway.ProvideHTTP(Address, grpcServer)
	if err = httpServer.Serve(tls.NewListener(listener, httpServer.TLSConfig)); err != nil {
		log.Fatal("ListenAndServe: ", err)
	}

使用postman測(cè)試

在動(dòng)圖中可以看到,我們的gRPC服務(wù)已經(jīng)同時(shí)支持RPC和HTTP請(qǐng)求了,而且API接口支持bearer token驗(yàn)證和數(shù)據(jù)驗(yàn)證。為了方便對(duì)接,我們把API接口生成swagger文檔。

生成swagger文檔

simple.swagger.json

1.安裝protoc-gen-swagger

go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger

2.編譯生成simple.swagger.json

到simple.proto文件目錄下,編譯:

protoc --swagger_out=logtostderr=true:./ ./simple.proto

再次提一下,本人在VSCode中使用VSCode-proto3插件,第一篇有介紹,只要保存,就會(huì)自動(dòng)編譯,很方便,無(wú)需記憶指令。完整配置如下:

編譯生成后把需要的文件留下,不需要的刪掉。

把swagger-ui轉(zhuǎn)成Go代碼,備用

1.下載swagger-ui

下載地址,把dist目錄下的所有文件拷貝我們項(xiàng)目的server/swagger/swagger-ui/目錄下。

2.把Swagger UI轉(zhuǎn)換為Go代碼

安裝go-bindata:

go get -u github.com/jteeuwen/go-bindata/...

回到server/所在目錄,運(yùn)行指令把Swagger UI轉(zhuǎn)成Go代碼。

go-bindata --nocompress -pkg swagger -o swagger/datafile.go swagger/swagger-ui/...

這步有坑,必須要回到main函數(shù)所在的目錄運(yùn)行指令,因?yàn)樯傻腉o代碼中的_bindata 映射了swagger-ui的路徑,程序是根據(jù)這些路徑來(lái)找頁(yè)面的。如果沒(méi)有在main函數(shù)所在的目錄運(yùn)行指令,則生成的路徑不對(duì),會(huì)報(bào)404,無(wú)法找到頁(yè)面。本項(xiàng)目server/端的main函數(shù)在server.go中,所以在server/所在目錄下運(yùn)行指令。

var _bindata = map[string]func() (*asset, error){
	"swagger/swagger-ui/favicon-16x16.png": swaggerSwaggerUiFavicon16x16Png,
	"swagger/swagger-ui/favicon-32x32.png": swaggerSwaggerUiFavicon32x32Png,
	"swagger/swagger-ui/index.html": swaggerSwaggerUiIndexHtml,
	"swagger/swagger-ui/oauth2-redirect.html": swaggerSwaggerUiOauth2RedirectHtml,
	"swagger/swagger-ui/swagger-ui-bundle.js": swaggerSwaggerUiSwaggerUiBundleJs,
	"swagger/swagger-ui/swagger-ui-bundle.js.map": swaggerSwaggerUiSwaggerUiBundleJsMap,
	"swagger/swagger-ui/swagger-ui-standalone-preset.js": swaggerSwaggerUiSwaggerUiStandalonePresetJs,
	"swagger/swagger-ui/swagger-ui-standalone-preset.js.map": swaggerSwaggerUiSwaggerUiStandalonePresetJsMap,
	"swagger/swagger-ui/swagger-ui.css": swaggerSwaggerUiSwaggerUiCss,
	"swagger/swagger-ui/swagger-ui.css.map": swaggerSwaggerUiSwaggerUiCssMap,
	"swagger/swagger-ui/swagger-ui.js": swaggerSwaggerUiSwaggerUiJs,
	"swagger/swagger-ui/swagger-ui.js.map": swaggerSwaggerUiSwaggerUiJsMap,
}

對(duì)外提供swagger-ui

1.在swagger/目錄下新建swagger.go文件

package swagger
import (
	"log"
	"net/http"
	"path"
	"strings"
	assetfs "github.com/elazarl/go-bindata-assetfs"
)
//ServeSwaggerFile 把proto文件夾中的swagger.json文件暴露出去
func ServeSwaggerFile(w http.ResponseWriter, r *http.Request) {
	if !strings.HasSuffix(r.URL.Path, "swagger.json") {
		log.Printf("Not Found: %s", r.URL.Path)
		http.NotFound(w, r)
		return
	}
	p := strings.TrimPrefix(r.URL.Path, "/swagger/")
	// "../proto/"為.swagger.json所在目錄
	p = path.Join("../proto/", p)
	log.Printf("Serving swagger-file: %s", p)
	http.ServeFile(w, r, p)
}
//ServeSwaggerUI 對(duì)外提供swagger-ui
func ServeSwaggerUI(mux *http.ServeMux) {
	fileServer := http.FileServer(&assetfs.AssetFS{
		Asset:    Asset,
		AssetDir: AssetDir,
		Prefix:   "swagger/swagger-ui", //swagger-ui文件夾所在目錄
	})
	prefix := "/swagger-ui/"
	mux.Handle(prefix, http.StripPrefix(prefix, fileServer))
}

2.注冊(cè)swagger

在gateway.go中添加如下代碼

	//注冊(cè)swagger
	mux.HandleFunc("/swagger/", swagger.ServeSwaggerFile)
	swagger.ServeSwaggerUI(mux)

到這里我們已經(jīng)完成了swagger文檔的添加工作了,由于谷歌瀏覽器不能使用自己制作的TLS證書(shū),所以我們用火狐瀏覽器進(jìn)行測(cè)試。

用火狐瀏覽器打開(kāi):https://127.0.0.1:8000/swagger-ui/

在最上面地址欄輸入:https://127.0.0.1:8000/swagger/simple.swagger.json

然后就可以看到swagger生成的API文檔了。

還有個(gè)問(wèn)題,我們使用了bearer token進(jìn)行接口驗(yàn)證的,怎么把bearer token也添加到swagger中呢?
最后我在grpc-gatewayGitHub上的這個(gè)Issues找到解決辦法。

在swagger中配置bearer token

1.修改simple.proto文件

syntax = "proto3";
package proto;
import "github.com/mwitkow/go-proto-validators/validator.proto";
import "go-grpc-example/10-grpc-gateway/proto/google/api/annotations.proto";
import "go-grpc-example/10-grpc-gateway/proto/google/options/annotations.proto";
message InnerMessage {
  // some_integer can only be in range (1, 100).
  int32 some_integer = 1 [(validator.field) = {int_gt: 0, int_lt: 100}];
  // some_float can only be in range (0;1).
  double some_float = 2 [(validator.field) = {float_gte: 0, float_lte: 1}];
}
message OuterMessage {
  // important_string must be a lowercase alpha-numeric of 5 to 30 characters (RE2 syntax).
  string important_string = 1 [(validator.field) = {regex: "^[a-z]{2,5}$"}];
  // proto3 doesn't have `required`, the `msg_exist` enforces presence of InnerMessage.
  InnerMessage inner = 2 [(validator.field) = {msg_exists : true}];
}
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
  security_definitions: {
    security: {
      key: "bearer"
      value: {
        type: TYPE_API_KEY
        in: IN_HEADER
        name: "Authorization"
        description: "Authentication token, prefixed by Bearer: Bearer <token>"
      }
    }
  }
  security: {
    security_requirement: {
      key: "bearer"
    }
  }
  info: {
		title: "grpc gateway sample";
		version: "1.0";	
		license: {
			name: "MIT";			
		};
  }
  schemes: HTTPS
};
service Simple{
  rpc Route (InnerMessage) returns (OuterMessage){
      option (google.api.http) ={
          post:"/v1/example/route"
          body:"*"
      };
      // //禁用bearer token
      // option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = {
      //   security: { } // Disable security key
      // };
  }
}

2.重新編譯生成simple.swagger.json

大功告成!

驗(yàn)證測(cè)試

1.添加bearer token

2.調(diào)用接口,正確返回?cái)?shù)據(jù)

3.傳遞不合規(guī)則的數(shù)據(jù),返回違反數(shù)據(jù)驗(yàn)證邏輯錯(cuò)誤

總結(jié)

本篇介紹了如何使用grpc-gateway讓gRPC同時(shí)支持HTTP,最終轉(zhuǎn)成的Restful Api支持bearer token驗(yàn)證、數(shù)據(jù)驗(yàn)證。同時(shí)生成swagger文檔,方便API接口對(duì)接。

教程源碼地址:https://github.com/Bingjian-Zhu/go-grpc-example

參考文檔

http://www.fzitv.net/article/251828.htm

http://www.fzitv.net/article/251837.htm

以上就是Go gRPC進(jìn)階教程gRPC轉(zhuǎn)換HTTP的詳細(xì)內(nèi)容,更多關(guān)于Go gRPC轉(zhuǎn)換HTTP的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • GO中?分組聲明與array,?slice,?map函數(shù)

    GO中?分組聲明與array,?slice,?map函數(shù)

    這篇文章主要介紹了GO中?分組聲明與array,slice,map函數(shù),Go語(yǔ)言中,同時(shí)聲明多個(gè)常量、變量,或者導(dǎo)入多個(gè)包時(shí),可采用分組的方式進(jìn)行聲明,下面詳細(xì)介紹需要的小伙伴可以參考一下
    2022-03-03
  • Go程序性能優(yōu)化及pprof使用方法詳解

    Go程序性能優(yōu)化及pprof使用方法詳解

    這篇文章主要為大家詳細(xì)介紹了Go程序性能優(yōu)化及pprof的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • go?proto編譯引用外部包問(wèn)題解決方案示例

    go?proto編譯引用外部包問(wèn)題解決方案示例

    這篇文章主要為大家介紹了go?proto編譯引用外部包問(wèn)題解決方案示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • Go語(yǔ)言通過(guò)smtp發(fā)送郵件的方法

    Go語(yǔ)言通過(guò)smtp發(fā)送郵件的方法

    這篇文章主要介紹了Go語(yǔ)言通過(guò)smtp發(fā)送郵件的方法,涉及Go語(yǔ)言發(fā)送郵件的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • golang中sync.Mutex的實(shí)現(xiàn)方法

    golang中sync.Mutex的實(shí)現(xiàn)方法

    本文主要介紹了golang中sync.Mutex的實(shí)現(xiàn)方法,mutex?主要有兩個(gè)?method:?Lock()?和?Unlock(),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • 聊聊Golang中很好用的viper配置模塊

    聊聊Golang中很好用的viper配置模塊

    這篇文章主要介紹了Golang中很好用的viper配置模塊用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • golang如何使用gos7讀取S7200Smart數(shù)據(jù)

    golang如何使用gos7讀取S7200Smart數(shù)據(jù)

    文章介紹了如何使用Golang語(yǔ)言的Gos7工具庫(kù)讀取西門子S7200Smart系列PLC的數(shù)據(jù),通過(guò)指定數(shù)據(jù)塊號(hào)、起始字節(jié)偏移量和數(shù)據(jù)長(zhǎng)度,可以精確讀取所需的數(shù)據(jù),感興趣的朋友跟隨小編一起看看吧
    2024-12-12
  • 解析Golang中的鎖競(jìng)爭(zhēng)問(wèn)題

    解析Golang中的鎖競(jìng)爭(zhēng)問(wèn)題

    這篇文章主要介紹了golang中的鎖競(jìng)爭(zhēng)問(wèn)題,本文通過(guò)實(shí)例代碼給大家詳細(xì)講解,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-10-10
  • go中控制goroutine數(shù)量的方法

    go中控制goroutine數(shù)量的方法

    這篇文章主要介紹了go中控制goroutine數(shù)量的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • 利用Go語(yǔ)言開(kāi)發(fā)文件操作工具輕松處理所有文件

    利用Go語(yǔ)言開(kāi)發(fā)文件操作工具輕松處理所有文件

    在后端開(kāi)發(fā)中,文件操作是一個(gè)非常常見(jiàn)但又容易出錯(cuò)的場(chǎng)景,本文小編要向大家介紹一個(gè)強(qiáng)大的 Go 語(yǔ)言文件操作工具庫(kù),它能幫你輕松處理各種文件操作場(chǎng)景
    2025-03-03

最新評(píng)論

永春县| 信阳市| 潼关县| 杂多县| 偃师市| 林芝县| 舒兰市| 鄂托克前旗| 石阡县| 古浪县| 潞城市| 鄂伦春自治旗| 北川| 杂多县| 深水埗区| 临桂县| 安阳市| 西平县| 出国| 巴彦县| 左云县| 临夏市| 墨脱县| 湖州市| 斗六市| 台江县| 济阳县| 武宁县| 临城县| 南江县| 通城县| 乌拉特前旗| 博湖县| 汾阳市| 垦利县| 洛隆县| 嘉祥县| 娄底市| 岑巩县| 兴文县| 吉林市|