'grpc-gateway 系列2:Hello World'

OpenSSL Google Go語言中文網 2019-07-28
"

Go語言中文網,致力於每日分享編碼、開源等知識,歡迎關注我,會有意想不到的收穫!

"

Go語言中文網,致力於每日分享編碼、開源等知識,歡迎關注我,會有意想不到的收穫!

grpc-gateway 系列2:Hello World

這節將開始編寫一個複雜的Hello World,涉及到許多的知識,建議大家認真思考其中的概念

需求

由於本實踐偏向Grpc+Grpc Gateway的方面,我們的需求是同一個服務端支持Rpc和Restful Api,那麼就意味著http2、TLS等等的應用,功能方面就是一個服務端能夠接受來自grpc和Restful Api的請求並響應

一、初始化目錄

我們先在$GOPATH中新建grpc-hello-world文件夾,我們項目的初始目錄目錄如下:

grpc-hello-world/
├── certs
├── client
├── cmd
├── pkg
├── proto
│ ├── google
│ │ └── api
└── server
  • certs:證書憑證
  • client:客戶端
  • cmd:命令行
  • pkg:第三方公共模塊
  • proto:protobuf的一些相關文件(含.proto、pb.go、.pb.gw.go),google/api中用於存放annotations.proto、http.proto
  • server:服務端

二、製作證書

在服務端支持Rpc和Restful Api,需要用到TLS,因此我們要先製作證書

進入certs目錄,生成TLS所需的公鑰密鑰文件

私鑰

openssl genrsa -out server.key 2048
openssl ecparam -genkey -name secp384r1 -out server.key
  • openssl genrsa:生成RSA私鑰,命令的最後一個參數,將指定生成密鑰的位數,如果沒有指定,默認512
  • openssl ecparam:生成ECC私鑰,命令為橢圓曲線密鑰參數生成及操作,本文中ECC曲線選擇的是secp384r1

自簽名公鑰

openssl req -new -x509 -sha256 -key server.key -out server.pem -days 3650
  • openssl req:生成自簽名證書,-new指生成證書請求、-sha256指使用sha256加密、-key指定私鑰文件、-x509指輸出證書、-days 3650為有效期,此後則輸入證書擁有者信息

填寫信息

Country Name (2 letter code) [XX]:
State or Province Name (full name) []:
Locality Name (eg, city) [Default City]:
Organization Name (eg, company) [Default Company Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:grpc server name
Email Address []:

三、proto

編寫

1、 google.api

我們看到proto目錄中有google/api目錄,它用到了google官方提供的兩個api描述文件,主要是針對grpc-gateway的http轉換提供支持,定義了Protocol Buffer所擴展的HTTP Option

annotations.proto文件:

"

Go語言中文網,致力於每日分享編碼、開源等知識,歡迎關注我,會有意想不到的收穫!

grpc-gateway 系列2:Hello World

這節將開始編寫一個複雜的Hello World,涉及到許多的知識,建議大家認真思考其中的概念

需求

由於本實踐偏向Grpc+Grpc Gateway的方面,我們的需求是同一個服務端支持Rpc和Restful Api,那麼就意味著http2、TLS等等的應用,功能方面就是一個服務端能夠接受來自grpc和Restful Api的請求並響應

一、初始化目錄

我們先在$GOPATH中新建grpc-hello-world文件夾,我們項目的初始目錄目錄如下:

grpc-hello-world/
├── certs
├── client
├── cmd
├── pkg
├── proto
│ ├── google
│ │ └── api
└── server
  • certs:證書憑證
  • client:客戶端
  • cmd:命令行
  • pkg:第三方公共模塊
  • proto:protobuf的一些相關文件(含.proto、pb.go、.pb.gw.go),google/api中用於存放annotations.proto、http.proto
  • server:服務端

二、製作證書

在服務端支持Rpc和Restful Api,需要用到TLS,因此我們要先製作證書

進入certs目錄,生成TLS所需的公鑰密鑰文件

私鑰

openssl genrsa -out server.key 2048
openssl ecparam -genkey -name secp384r1 -out server.key
  • openssl genrsa:生成RSA私鑰,命令的最後一個參數,將指定生成密鑰的位數,如果沒有指定,默認512
  • openssl ecparam:生成ECC私鑰,命令為橢圓曲線密鑰參數生成及操作,本文中ECC曲線選擇的是secp384r1

自簽名公鑰

openssl req -new -x509 -sha256 -key server.key -out server.pem -days 3650
  • openssl req:生成自簽名證書,-new指生成證書請求、-sha256指使用sha256加密、-key指定私鑰文件、-x509指輸出證書、-days 3650為有效期,此後則輸入證書擁有者信息

填寫信息

Country Name (2 letter code) [XX]:
State or Province Name (full name) []:
Locality Name (eg, city) [Default City]:
Organization Name (eg, company) [Default Company Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:grpc server name
Email Address []:

三、proto

編寫

1、 google.api

我們看到proto目錄中有google/api目錄,它用到了google官方提供的兩個api描述文件,主要是針對grpc-gateway的http轉換提供支持,定義了Protocol Buffer所擴展的HTTP Option

annotations.proto文件:

grpc-gateway 系列2:Hello World

http.proto文件:

"

Go語言中文網,致力於每日分享編碼、開源等知識,歡迎關注我,會有意想不到的收穫!

grpc-gateway 系列2:Hello World

這節將開始編寫一個複雜的Hello World,涉及到許多的知識,建議大家認真思考其中的概念

需求

由於本實踐偏向Grpc+Grpc Gateway的方面,我們的需求是同一個服務端支持Rpc和Restful Api,那麼就意味著http2、TLS等等的應用,功能方面就是一個服務端能夠接受來自grpc和Restful Api的請求並響應

一、初始化目錄

我們先在$GOPATH中新建grpc-hello-world文件夾,我們項目的初始目錄目錄如下:

grpc-hello-world/
├── certs
├── client
├── cmd
├── pkg
├── proto
│ ├── google
│ │ └── api
└── server
  • certs:證書憑證
  • client:客戶端
  • cmd:命令行
  • pkg:第三方公共模塊
  • proto:protobuf的一些相關文件(含.proto、pb.go、.pb.gw.go),google/api中用於存放annotations.proto、http.proto
  • server:服務端

二、製作證書

在服務端支持Rpc和Restful Api,需要用到TLS,因此我們要先製作證書

進入certs目錄,生成TLS所需的公鑰密鑰文件

私鑰

openssl genrsa -out server.key 2048
openssl ecparam -genkey -name secp384r1 -out server.key
  • openssl genrsa:生成RSA私鑰,命令的最後一個參數,將指定生成密鑰的位數,如果沒有指定,默認512
  • openssl ecparam:生成ECC私鑰,命令為橢圓曲線密鑰參數生成及操作,本文中ECC曲線選擇的是secp384r1

自簽名公鑰

openssl req -new -x509 -sha256 -key server.key -out server.pem -days 3650
  • openssl req:生成自簽名證書,-new指生成證書請求、-sha256指使用sha256加密、-key指定私鑰文件、-x509指輸出證書、-days 3650為有效期,此後則輸入證書擁有者信息

填寫信息

Country Name (2 letter code) [XX]:
State or Province Name (full name) []:
Locality Name (eg, city) [Default City]:
Organization Name (eg, company) [Default Company Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:grpc server name
Email Address []:

三、proto

編寫

1、 google.api

我們看到proto目錄中有google/api目錄,它用到了google官方提供的兩個api描述文件,主要是針對grpc-gateway的http轉換提供支持,定義了Protocol Buffer所擴展的HTTP Option

annotations.proto文件:

grpc-gateway 系列2:Hello World

http.proto文件:

grpc-gateway 系列2:Hello World

  1. hello.proto

這一小節將編寫Demo的.proto文件,我們在proto目錄下新建hello.proto文件,寫入文件內容:

"

Go語言中文網,致力於每日分享編碼、開源等知識,歡迎關注我,會有意想不到的收穫!

grpc-gateway 系列2:Hello World

這節將開始編寫一個複雜的Hello World,涉及到許多的知識,建議大家認真思考其中的概念

需求

由於本實踐偏向Grpc+Grpc Gateway的方面,我們的需求是同一個服務端支持Rpc和Restful Api,那麼就意味著http2、TLS等等的應用,功能方面就是一個服務端能夠接受來自grpc和Restful Api的請求並響應

一、初始化目錄

我們先在$GOPATH中新建grpc-hello-world文件夾,我們項目的初始目錄目錄如下:

grpc-hello-world/
├── certs
├── client
├── cmd
├── pkg
├── proto
│ ├── google
│ │ └── api
└── server
  • certs:證書憑證
  • client:客戶端
  • cmd:命令行
  • pkg:第三方公共模塊
  • proto:protobuf的一些相關文件(含.proto、pb.go、.pb.gw.go),google/api中用於存放annotations.proto、http.proto
  • server:服務端

二、製作證書

在服務端支持Rpc和Restful Api,需要用到TLS,因此我們要先製作證書

進入certs目錄,生成TLS所需的公鑰密鑰文件

私鑰

openssl genrsa -out server.key 2048
openssl ecparam -genkey -name secp384r1 -out server.key
  • openssl genrsa:生成RSA私鑰,命令的最後一個參數,將指定生成密鑰的位數,如果沒有指定,默認512
  • openssl ecparam:生成ECC私鑰,命令為橢圓曲線密鑰參數生成及操作,本文中ECC曲線選擇的是secp384r1

自簽名公鑰

openssl req -new -x509 -sha256 -key server.key -out server.pem -days 3650
  • openssl req:生成自簽名證書,-new指生成證書請求、-sha256指使用sha256加密、-key指定私鑰文件、-x509指輸出證書、-days 3650為有效期,此後則輸入證書擁有者信息

填寫信息

Country Name (2 letter code) [XX]:
State or Province Name (full name) []:
Locality Name (eg, city) [Default City]:
Organization Name (eg, company) [Default Company Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:grpc server name
Email Address []:

三、proto

編寫

1、 google.api

我們看到proto目錄中有google/api目錄,它用到了google官方提供的兩個api描述文件,主要是針對grpc-gateway的http轉換提供支持,定義了Protocol Buffer所擴展的HTTP Option

annotations.proto文件:

grpc-gateway 系列2:Hello World

http.proto文件:

grpc-gateway 系列2:Hello World

  1. hello.proto

這一小節將編寫Demo的.proto文件,我們在proto目錄下新建hello.proto文件,寫入文件內容:

grpc-gateway 系列2:Hello World

在hello.proto文件中,引用了google/api/annotations.proto,達到支持HTTP Option的效果

  • 定義了一個serviceRPC服務HelloWorld,在其內部定義了一個HTTP Option的POST方法,HTTP響應路徑為/hello_world
  • 定義message類型HelloWorldRequest、HelloWorldResponse,用於響應請求和返回結果

編譯

在編寫完.proto文件後,我們需要對其進行編譯,就能夠在server中使用

進入proto目錄,執行以下命令

# 編譯google.api
protoc -I . --go_out=plugins=grpc,Mgoogle/protobuf/descriptor.proto=github.com/golang/protobuf/protoc-gen-go/descriptor:. google/api/*.proto
#編譯hello_http.proto為hello_http.pb.proto
protoc -I . --go_out=plugins=grpc,Mgoogle/api/annotations.proto=grpc-hello-world/proto/google/api:. ./hello.proto
#編譯hello_http.proto為hello_http.pb.gw.proto
protoc --grpc-gateway_out=logtostderr=true:. ./hello.proto

執行完畢後將生成hello.pb.go和hello.gw.pb.go,分別針對grpc和grpc-gateway的功能支持

四、命令行模塊 cmd

介紹

這一小節我們編寫命令行模塊,為什麼要獨立出來呢,是為了將cmd和server兩者解耦,避免混淆在一起。

我們採用 Cobra 來完成這項功能,Cobra既是創建強大的現代CLI應用程序的庫,也是生成應用程序和命令文件的程序。提供了以下功能:

  • 簡易的子命令行模式
  • 完全兼容posix的命令行模式(包括短和長版本)
  • 嵌套的子命令
  • 全局、本地和級聯flags
  • 使用Cobra很容易的生成應用程序和命令,使用cobra create appname和cobra add cmdname
  • 智能提示
  • 自動生成commands和flags的幫助信息
  • 自動生成詳細的help信息-h,--help等等
  • 自動生成的bash自動完成功能
  • 為應用程序自動生成手冊
  • 命令別名
  • 定義您自己的幫助、用法等的靈活性。
  • 可選與viper緊密集成的apps

編寫server

在編寫cmd時需要先用server進行測試關聯,因此這一步我們先寫server.go用於測試

在server模塊下 新建server.go文件,寫入測試內容:

"

Go語言中文網,致力於每日分享編碼、開源等知識,歡迎關注我,會有意想不到的收穫!

grpc-gateway 系列2:Hello World

這節將開始編寫一個複雜的Hello World,涉及到許多的知識,建議大家認真思考其中的概念

需求

由於本實踐偏向Grpc+Grpc Gateway的方面,我們的需求是同一個服務端支持Rpc和Restful Api,那麼就意味著http2、TLS等等的應用,功能方面就是一個服務端能夠接受來自grpc和Restful Api的請求並響應

一、初始化目錄

我們先在$GOPATH中新建grpc-hello-world文件夾,我們項目的初始目錄目錄如下:

grpc-hello-world/
├── certs
├── client
├── cmd
├── pkg
├── proto
│ ├── google
│ │ └── api
└── server
  • certs:證書憑證
  • client:客戶端
  • cmd:命令行
  • pkg:第三方公共模塊
  • proto:protobuf的一些相關文件(含.proto、pb.go、.pb.gw.go),google/api中用於存放annotations.proto、http.proto
  • server:服務端

二、製作證書

在服務端支持Rpc和Restful Api,需要用到TLS,因此我們要先製作證書

進入certs目錄,生成TLS所需的公鑰密鑰文件

私鑰

openssl genrsa -out server.key 2048
openssl ecparam -genkey -name secp384r1 -out server.key
  • openssl genrsa:生成RSA私鑰,命令的最後一個參數,將指定生成密鑰的位數,如果沒有指定,默認512
  • openssl ecparam:生成ECC私鑰,命令為橢圓曲線密鑰參數生成及操作,本文中ECC曲線選擇的是secp384r1

自簽名公鑰

openssl req -new -x509 -sha256 -key server.key -out server.pem -days 3650
  • openssl req:生成自簽名證書,-new指生成證書請求、-sha256指使用sha256加密、-key指定私鑰文件、-x509指輸出證書、-days 3650為有效期,此後則輸入證書擁有者信息

填寫信息

Country Name (2 letter code) [XX]:
State or Province Name (full name) []:
Locality Name (eg, city) [Default City]:
Organization Name (eg, company) [Default Company Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:grpc server name
Email Address []:

三、proto

編寫

1、 google.api

我們看到proto目錄中有google/api目錄,它用到了google官方提供的兩個api描述文件,主要是針對grpc-gateway的http轉換提供支持,定義了Protocol Buffer所擴展的HTTP Option

annotations.proto文件:

grpc-gateway 系列2:Hello World

http.proto文件:

grpc-gateway 系列2:Hello World

  1. hello.proto

這一小節將編寫Demo的.proto文件,我們在proto目錄下新建hello.proto文件,寫入文件內容:

grpc-gateway 系列2:Hello World

在hello.proto文件中,引用了google/api/annotations.proto,達到支持HTTP Option的效果

  • 定義了一個serviceRPC服務HelloWorld,在其內部定義了一個HTTP Option的POST方法,HTTP響應路徑為/hello_world
  • 定義message類型HelloWorldRequest、HelloWorldResponse,用於響應請求和返回結果

編譯

在編寫完.proto文件後,我們需要對其進行編譯,就能夠在server中使用

進入proto目錄,執行以下命令

# 編譯google.api
protoc -I . --go_out=plugins=grpc,Mgoogle/protobuf/descriptor.proto=github.com/golang/protobuf/protoc-gen-go/descriptor:. google/api/*.proto
#編譯hello_http.proto為hello_http.pb.proto
protoc -I . --go_out=plugins=grpc,Mgoogle/api/annotations.proto=grpc-hello-world/proto/google/api:. ./hello.proto
#編譯hello_http.proto為hello_http.pb.gw.proto
protoc --grpc-gateway_out=logtostderr=true:. ./hello.proto

執行完畢後將生成hello.pb.go和hello.gw.pb.go,分別針對grpc和grpc-gateway的功能支持

四、命令行模塊 cmd

介紹

這一小節我們編寫命令行模塊,為什麼要獨立出來呢,是為了將cmd和server兩者解耦,避免混淆在一起。

我們採用 Cobra 來完成這項功能,Cobra既是創建強大的現代CLI應用程序的庫,也是生成應用程序和命令文件的程序。提供了以下功能:

  • 簡易的子命令行模式
  • 完全兼容posix的命令行模式(包括短和長版本)
  • 嵌套的子命令
  • 全局、本地和級聯flags
  • 使用Cobra很容易的生成應用程序和命令,使用cobra create appname和cobra add cmdname
  • 智能提示
  • 自動生成commands和flags的幫助信息
  • 自動生成詳細的help信息-h,--help等等
  • 自動生成的bash自動完成功能
  • 為應用程序自動生成手冊
  • 命令別名
  • 定義您自己的幫助、用法等的靈活性。
  • 可選與viper緊密集成的apps

編寫server

在編寫cmd時需要先用server進行測試關聯,因此這一步我們先寫server.go用於測試

在server模塊下 新建server.go文件,寫入測試內容:

grpc-gateway 系列2:Hello World

編寫cmd

在cmd模塊下 新建root.go文件,寫入內容:

"

Go語言中文網,致力於每日分享編碼、開源等知識,歡迎關注我,會有意想不到的收穫!

grpc-gateway 系列2:Hello World

這節將開始編寫一個複雜的Hello World,涉及到許多的知識,建議大家認真思考其中的概念

需求

由於本實踐偏向Grpc+Grpc Gateway的方面,我們的需求是同一個服務端支持Rpc和Restful Api,那麼就意味著http2、TLS等等的應用,功能方面就是一個服務端能夠接受來自grpc和Restful Api的請求並響應

一、初始化目錄

我們先在$GOPATH中新建grpc-hello-world文件夾,我們項目的初始目錄目錄如下:

grpc-hello-world/
├── certs
├── client
├── cmd
├── pkg
├── proto
│ ├── google
│ │ └── api
└── server
  • certs:證書憑證
  • client:客戶端
  • cmd:命令行
  • pkg:第三方公共模塊
  • proto:protobuf的一些相關文件(含.proto、pb.go、.pb.gw.go),google/api中用於存放annotations.proto、http.proto
  • server:服務端

二、製作證書

在服務端支持Rpc和Restful Api,需要用到TLS,因此我們要先製作證書

進入certs目錄,生成TLS所需的公鑰密鑰文件

私鑰

openssl genrsa -out server.key 2048
openssl ecparam -genkey -name secp384r1 -out server.key
  • openssl genrsa:生成RSA私鑰,命令的最後一個參數,將指定生成密鑰的位數,如果沒有指定,默認512
  • openssl ecparam:生成ECC私鑰,命令為橢圓曲線密鑰參數生成及操作,本文中ECC曲線選擇的是secp384r1

自簽名公鑰

openssl req -new -x509 -sha256 -key server.key -out server.pem -days 3650
  • openssl req:生成自簽名證書,-new指生成證書請求、-sha256指使用sha256加密、-key指定私鑰文件、-x509指輸出證書、-days 3650為有效期,此後則輸入證書擁有者信息

填寫信息

Country Name (2 letter code) [XX]:
State or Province Name (full name) []:
Locality Name (eg, city) [Default City]:
Organization Name (eg, company) [Default Company Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:grpc server name
Email Address []:

三、proto

編寫

1、 google.api

我們看到proto目錄中有google/api目錄,它用到了google官方提供的兩個api描述文件,主要是針對grpc-gateway的http轉換提供支持,定義了Protocol Buffer所擴展的HTTP Option

annotations.proto文件:

grpc-gateway 系列2:Hello World

http.proto文件:

grpc-gateway 系列2:Hello World

  1. hello.proto

這一小節將編寫Demo的.proto文件,我們在proto目錄下新建hello.proto文件,寫入文件內容:

grpc-gateway 系列2:Hello World

在hello.proto文件中,引用了google/api/annotations.proto,達到支持HTTP Option的效果

  • 定義了一個serviceRPC服務HelloWorld,在其內部定義了一個HTTP Option的POST方法,HTTP響應路徑為/hello_world
  • 定義message類型HelloWorldRequest、HelloWorldResponse,用於響應請求和返回結果

編譯

在編寫完.proto文件後,我們需要對其進行編譯,就能夠在server中使用

進入proto目錄,執行以下命令

# 編譯google.api
protoc -I . --go_out=plugins=grpc,Mgoogle/protobuf/descriptor.proto=github.com/golang/protobuf/protoc-gen-go/descriptor:. google/api/*.proto
#編譯hello_http.proto為hello_http.pb.proto
protoc -I . --go_out=plugins=grpc,Mgoogle/api/annotations.proto=grpc-hello-world/proto/google/api:. ./hello.proto
#編譯hello_http.proto為hello_http.pb.gw.proto
protoc --grpc-gateway_out=logtostderr=true:. ./hello.proto

執行完畢後將生成hello.pb.go和hello.gw.pb.go,分別針對grpc和grpc-gateway的功能支持

四、命令行模塊 cmd

介紹

這一小節我們編寫命令行模塊,為什麼要獨立出來呢,是為了將cmd和server兩者解耦,避免混淆在一起。

我們採用 Cobra 來完成這項功能,Cobra既是創建強大的現代CLI應用程序的庫,也是生成應用程序和命令文件的程序。提供了以下功能:

  • 簡易的子命令行模式
  • 完全兼容posix的命令行模式(包括短和長版本)
  • 嵌套的子命令
  • 全局、本地和級聯flags
  • 使用Cobra很容易的生成應用程序和命令,使用cobra create appname和cobra add cmdname
  • 智能提示
  • 自動生成commands和flags的幫助信息
  • 自動生成詳細的help信息-h,--help等等
  • 自動生成的bash自動完成功能
  • 為應用程序自動生成手冊
  • 命令別名
  • 定義您自己的幫助、用法等的靈活性。
  • 可選與viper緊密集成的apps

編寫server

在編寫cmd時需要先用server進行測試關聯,因此這一步我們先寫server.go用於測試

在server模塊下 新建server.go文件,寫入測試內容:

grpc-gateway 系列2:Hello World

編寫cmd

在cmd模塊下 新建root.go文件,寫入內容:

grpc-gateway 系列2:Hello World

新建server.go文件,寫入內容:

package cmd
import (
"log"
"github.com/spf13/cobra"
"grpc-hello-world/server"
)
var serverCmd = &cobra.Command{
Use: "server",
Short: "Run the gRPC hello-world server",
Run: func(cmd *cobra.Command, args []string) {
defer func() {
if err := recover(); err != nil {
log.Println("Recover error : %v", err)
}
}()
server.Serve()
},
}
func init() {
serverCmd.Flags().StringVarP(&server.ServerPort, "port", "p", "50052", "server port")
serverCmd.Flags().StringVarP(&server.CertPemPath, "cert-pem", "", "./certs/server.pem", "cert pem path")
serverCmd.Flags().StringVarP(&server.CertKeyPath, "cert-key", "", "./certs/server.key", "cert key path")
serverCmd.Flags().StringVarP(&server.CertName, "cert-name", "", "grpc server name", "server's hostname")
rootCmd.AddCommand(serverCmd)
}

我們在grpc-hello-world/目錄下,新建文件main.go,寫入內容:

package main
import (
"grpc-hello-world/cmd"
)
func main() {
cmd.Execute()
}

講解

要使用Cobra,按照Cobra標準要創建main.go和一個rootCmd文件,另外我們有子命令server

1、rootCmd: rootCmd表示在沒有任何子命令的情況下的基本命令

2、&cobra.Command:

  • Use:Command的用法,Use是一個行用法消息
  • Short:Short是help命令輸出中顯示的簡短描述
  • Run:運行:典型的實際工作功能。大多數命令只會實現這一點;另外還有PreRun、PreRunE、PostRun、PostRunE等等不同時期的運行命令,但比較少用,具體使用時再查看亦可

3、rootCmd.AddCommand:AddCommand向這父命令(rootCmd)添加一個或多個命令

4、serverCmd.Flags().StringVarP():

一般來說,我們需要在init()函數中定義flags和處理配置,以serverCmd.Flags().StringVarP(&server.ServerPort, "port", "p", "50052", "server port")為例,我們定義了一個flag,值存儲在&server.ServerPort中,長命令為--port,短命令為-p,,默認值為50052,命令的描述為server port。這一種調用方式成為Local Flags

我們延伸一下,如果覺得每一個子命令都要設一遍覺得很麻煩,我們可以採用Persistent Flags:

rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")

作用:

flag是可以持久的,這意味著該flag將被分配給它所分配的命令以及該命令下的每個命令。對於全局標記,將標記作為根上的持久標誌。

另外還有Local Flag on Parent Commands、Bind Flags with Config、Required flags等等,使用到再 傳送 瞭解即可

測試

回到grpc-hello-world/目錄下執行go run main.go server,查看輸出是否為(此時應為默認值):

2018/02/25 23:23:21 50052
2018/02/25 23:23:21 dev
2018/02/25 23:23:21 ./certs/server.pem
2018/02/25 23:23:21 ./certs/server.key

執行go run main.go server --port=8000 --cert-pem=test-pem --cert-key=test-key --cert-name=test-name,檢驗命令行參數是否正確:

2018/02/25 23:24:56 8000
2018/02/25 23:24:56 test-name
2018/02/25 23:24:56 test-pem
2018/02/25 23:24:56 test-key

若都無誤,那麼恭喜你cmd模塊的編寫正確了,下一部分開始我們的重點章節!

五、服務端模塊 server

編寫hello.go

在server目錄下新建文件hello.go,寫入文件內容:

package server
import (
"golang.org/x/net/context"
pb "grpc-hello-world/proto"
)
type helloService struct{}
func NewHelloService() *helloService {
return &helloService{}
}
func (h helloService) SayHelloWorld(ctx context.Context, r *pb.HelloWorldRequest) (*pb.HelloWorldResponse, error) {
return &pb.HelloWorldResponse{
Message : "test",
}, nil
}

我們創建了helloService及其方法SayHelloWorld,對應.proto的rpc SayHelloWorld,這個方法需要有2個參數:ctx context.Context用於接受上下文參數、r *pb.HelloWorldRequest用於接受protobuf的Request參數(對應.proto的message HelloWorldRequest)

*編寫server.go

這一小章節,我們編寫最為重要的服務端程序部分,涉及到大量的grpc、grpc-gateway及一些網絡知識的應用

1、在pkg下新建util目錄,新建grpc.go文件,寫入內容:

package util
import (
"net/http"
"strings"
"google.golang.org/grpc"
)
func GrpcHandlerFunc(grpcServer *grpc.Server, otherHandler http.Handler) http.Handler {
if otherHandler == nil {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
grpcServer.ServeHTTP(w, r)
})
}
return 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)
}
})
}

GrpcHandlerFunc函數是用於判斷請求是來源於Rpc客戶端還是Restful Api的請求,根據不同的請求註冊不同的ServeHTTP服務;r.ProtoMajor == 2也代表著請求必須基於HTTP/2

2、在pkg下的util目錄下,新建tls.go文件,寫入內容:

package util
import (
"crypto/tls"
"io/ioutil"
"log"
"golang.org/x/net/http2"
)
func GetTLSConfig(certPemPath, certKeyPath string) *tls.Config {
var certKeyPair *tls.Certificate
cert, _ := ioutil.ReadFile(certPemPath)
key, _ := ioutil.ReadFile(certKeyPath)
pair, err := tls.X509KeyPair(cert, key)
if err != nil {
log.Println("TLS KeyPair err: %v\\n", err)
}
certKeyPair = &pair
return &tls.Config{
Certificates: []tls.Certificate{*certKeyPair},
NextProtos: []string{http2.NextProtoTLS},
}
}

GetTLSConfig函數是用於獲取TLS配置,在內部,我們讀取了server.key和server.pem這類證書憑證文件

  • tls.X509KeyPair:從一對PEM編碼的數據中解析公鑰/私鑰對。成功則返回公鑰/私鑰對
  • http2.NextProtoTLS:NextProtoTLS是談判期間的NPN/ALPN協議,用於HTTP/2的TLS設置
  • tls.Certificate:返回一個或多個證書,實質我們解析PEM調用的X509KeyPair的函數聲明就是func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error),返回值就是Certificate

總的來說該函數是用於處理從證書憑證文件(PEM),最終獲取tls.Config作為HTTP2的使用參數

3、修改server目錄下的server.go文件,該文件是我們服務裡的核心文件,寫入內容:

"

Go語言中文網,致力於每日分享編碼、開源等知識,歡迎關注我,會有意想不到的收穫!

grpc-gateway 系列2:Hello World

這節將開始編寫一個複雜的Hello World,涉及到許多的知識,建議大家認真思考其中的概念

需求

由於本實踐偏向Grpc+Grpc Gateway的方面,我們的需求是同一個服務端支持Rpc和Restful Api,那麼就意味著http2、TLS等等的應用,功能方面就是一個服務端能夠接受來自grpc和Restful Api的請求並響應

一、初始化目錄

我們先在$GOPATH中新建grpc-hello-world文件夾,我們項目的初始目錄目錄如下:

grpc-hello-world/
├── certs
├── client
├── cmd
├── pkg
├── proto
│ ├── google
│ │ └── api
└── server
  • certs:證書憑證
  • client:客戶端
  • cmd:命令行
  • pkg:第三方公共模塊
  • proto:protobuf的一些相關文件(含.proto、pb.go、.pb.gw.go),google/api中用於存放annotations.proto、http.proto
  • server:服務端

二、製作證書

在服務端支持Rpc和Restful Api,需要用到TLS,因此我們要先製作證書

進入certs目錄,生成TLS所需的公鑰密鑰文件

私鑰

openssl genrsa -out server.key 2048
openssl ecparam -genkey -name secp384r1 -out server.key
  • openssl genrsa:生成RSA私鑰,命令的最後一個參數,將指定生成密鑰的位數,如果沒有指定,默認512
  • openssl ecparam:生成ECC私鑰,命令為橢圓曲線密鑰參數生成及操作,本文中ECC曲線選擇的是secp384r1

自簽名公鑰

openssl req -new -x509 -sha256 -key server.key -out server.pem -days 3650
  • openssl req:生成自簽名證書,-new指生成證書請求、-sha256指使用sha256加密、-key指定私鑰文件、-x509指輸出證書、-days 3650為有效期,此後則輸入證書擁有者信息

填寫信息

Country Name (2 letter code) [XX]:
State or Province Name (full name) []:
Locality Name (eg, city) [Default City]:
Organization Name (eg, company) [Default Company Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:grpc server name
Email Address []:

三、proto

編寫

1、 google.api

我們看到proto目錄中有google/api目錄,它用到了google官方提供的兩個api描述文件,主要是針對grpc-gateway的http轉換提供支持,定義了Protocol Buffer所擴展的HTTP Option

annotations.proto文件:

grpc-gateway 系列2:Hello World

http.proto文件:

grpc-gateway 系列2:Hello World

  1. hello.proto

這一小節將編寫Demo的.proto文件,我們在proto目錄下新建hello.proto文件,寫入文件內容:

grpc-gateway 系列2:Hello World

在hello.proto文件中,引用了google/api/annotations.proto,達到支持HTTP Option的效果

  • 定義了一個serviceRPC服務HelloWorld,在其內部定義了一個HTTP Option的POST方法,HTTP響應路徑為/hello_world
  • 定義message類型HelloWorldRequest、HelloWorldResponse,用於響應請求和返回結果

編譯

在編寫完.proto文件後,我們需要對其進行編譯,就能夠在server中使用

進入proto目錄,執行以下命令

# 編譯google.api
protoc -I . --go_out=plugins=grpc,Mgoogle/protobuf/descriptor.proto=github.com/golang/protobuf/protoc-gen-go/descriptor:. google/api/*.proto
#編譯hello_http.proto為hello_http.pb.proto
protoc -I . --go_out=plugins=grpc,Mgoogle/api/annotations.proto=grpc-hello-world/proto/google/api:. ./hello.proto
#編譯hello_http.proto為hello_http.pb.gw.proto
protoc --grpc-gateway_out=logtostderr=true:. ./hello.proto

執行完畢後將生成hello.pb.go和hello.gw.pb.go,分別針對grpc和grpc-gateway的功能支持

四、命令行模塊 cmd

介紹

這一小節我們編寫命令行模塊,為什麼要獨立出來呢,是為了將cmd和server兩者解耦,避免混淆在一起。

我們採用 Cobra 來完成這項功能,Cobra既是創建強大的現代CLI應用程序的庫,也是生成應用程序和命令文件的程序。提供了以下功能:

  • 簡易的子命令行模式
  • 完全兼容posix的命令行模式(包括短和長版本)
  • 嵌套的子命令
  • 全局、本地和級聯flags
  • 使用Cobra很容易的生成應用程序和命令,使用cobra create appname和cobra add cmdname
  • 智能提示
  • 自動生成commands和flags的幫助信息
  • 自動生成詳細的help信息-h,--help等等
  • 自動生成的bash自動完成功能
  • 為應用程序自動生成手冊
  • 命令別名
  • 定義您自己的幫助、用法等的靈活性。
  • 可選與viper緊密集成的apps

編寫server

在編寫cmd時需要先用server進行測試關聯,因此這一步我們先寫server.go用於測試

在server模塊下 新建server.go文件,寫入測試內容:

grpc-gateway 系列2:Hello World

編寫cmd

在cmd模塊下 新建root.go文件,寫入內容:

grpc-gateway 系列2:Hello World

新建server.go文件,寫入內容:

package cmd
import (
"log"
"github.com/spf13/cobra"
"grpc-hello-world/server"
)
var serverCmd = &cobra.Command{
Use: "server",
Short: "Run the gRPC hello-world server",
Run: func(cmd *cobra.Command, args []string) {
defer func() {
if err := recover(); err != nil {
log.Println("Recover error : %v", err)
}
}()
server.Serve()
},
}
func init() {
serverCmd.Flags().StringVarP(&server.ServerPort, "port", "p", "50052", "server port")
serverCmd.Flags().StringVarP(&server.CertPemPath, "cert-pem", "", "./certs/server.pem", "cert pem path")
serverCmd.Flags().StringVarP(&server.CertKeyPath, "cert-key", "", "./certs/server.key", "cert key path")
serverCmd.Flags().StringVarP(&server.CertName, "cert-name", "", "grpc server name", "server's hostname")
rootCmd.AddCommand(serverCmd)
}

我們在grpc-hello-world/目錄下,新建文件main.go,寫入內容:

package main
import (
"grpc-hello-world/cmd"
)
func main() {
cmd.Execute()
}

講解

要使用Cobra,按照Cobra標準要創建main.go和一個rootCmd文件,另外我們有子命令server

1、rootCmd: rootCmd表示在沒有任何子命令的情況下的基本命令

2、&cobra.Command:

  • Use:Command的用法,Use是一個行用法消息
  • Short:Short是help命令輸出中顯示的簡短描述
  • Run:運行:典型的實際工作功能。大多數命令只會實現這一點;另外還有PreRun、PreRunE、PostRun、PostRunE等等不同時期的運行命令,但比較少用,具體使用時再查看亦可

3、rootCmd.AddCommand:AddCommand向這父命令(rootCmd)添加一個或多個命令

4、serverCmd.Flags().StringVarP():

一般來說,我們需要在init()函數中定義flags和處理配置,以serverCmd.Flags().StringVarP(&server.ServerPort, "port", "p", "50052", "server port")為例,我們定義了一個flag,值存儲在&server.ServerPort中,長命令為--port,短命令為-p,,默認值為50052,命令的描述為server port。這一種調用方式成為Local Flags

我們延伸一下,如果覺得每一個子命令都要設一遍覺得很麻煩,我們可以採用Persistent Flags:

rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")

作用:

flag是可以持久的,這意味著該flag將被分配給它所分配的命令以及該命令下的每個命令。對於全局標記,將標記作為根上的持久標誌。

另外還有Local Flag on Parent Commands、Bind Flags with Config、Required flags等等,使用到再 傳送 瞭解即可

測試

回到grpc-hello-world/目錄下執行go run main.go server,查看輸出是否為(此時應為默認值):

2018/02/25 23:23:21 50052
2018/02/25 23:23:21 dev
2018/02/25 23:23:21 ./certs/server.pem
2018/02/25 23:23:21 ./certs/server.key

執行go run main.go server --port=8000 --cert-pem=test-pem --cert-key=test-key --cert-name=test-name,檢驗命令行參數是否正確:

2018/02/25 23:24:56 8000
2018/02/25 23:24:56 test-name
2018/02/25 23:24:56 test-pem
2018/02/25 23:24:56 test-key

若都無誤,那麼恭喜你cmd模塊的編寫正確了,下一部分開始我們的重點章節!

五、服務端模塊 server

編寫hello.go

在server目錄下新建文件hello.go,寫入文件內容:

package server
import (
"golang.org/x/net/context"
pb "grpc-hello-world/proto"
)
type helloService struct{}
func NewHelloService() *helloService {
return &helloService{}
}
func (h helloService) SayHelloWorld(ctx context.Context, r *pb.HelloWorldRequest) (*pb.HelloWorldResponse, error) {
return &pb.HelloWorldResponse{
Message : "test",
}, nil
}

我們創建了helloService及其方法SayHelloWorld,對應.proto的rpc SayHelloWorld,這個方法需要有2個參數:ctx context.Context用於接受上下文參數、r *pb.HelloWorldRequest用於接受protobuf的Request參數(對應.proto的message HelloWorldRequest)

*編寫server.go

這一小章節,我們編寫最為重要的服務端程序部分,涉及到大量的grpc、grpc-gateway及一些網絡知識的應用

1、在pkg下新建util目錄,新建grpc.go文件,寫入內容:

package util
import (
"net/http"
"strings"
"google.golang.org/grpc"
)
func GrpcHandlerFunc(grpcServer *grpc.Server, otherHandler http.Handler) http.Handler {
if otherHandler == nil {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
grpcServer.ServeHTTP(w, r)
})
}
return 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)
}
})
}

GrpcHandlerFunc函數是用於判斷請求是來源於Rpc客戶端還是Restful Api的請求,根據不同的請求註冊不同的ServeHTTP服務;r.ProtoMajor == 2也代表著請求必須基於HTTP/2

2、在pkg下的util目錄下,新建tls.go文件,寫入內容:

package util
import (
"crypto/tls"
"io/ioutil"
"log"
"golang.org/x/net/http2"
)
func GetTLSConfig(certPemPath, certKeyPath string) *tls.Config {
var certKeyPair *tls.Certificate
cert, _ := ioutil.ReadFile(certPemPath)
key, _ := ioutil.ReadFile(certKeyPath)
pair, err := tls.X509KeyPair(cert, key)
if err != nil {
log.Println("TLS KeyPair err: %v\\n", err)
}
certKeyPair = &pair
return &tls.Config{
Certificates: []tls.Certificate{*certKeyPair},
NextProtos: []string{http2.NextProtoTLS},
}
}

GetTLSConfig函數是用於獲取TLS配置,在內部,我們讀取了server.key和server.pem這類證書憑證文件

  • tls.X509KeyPair:從一對PEM編碼的數據中解析公鑰/私鑰對。成功則返回公鑰/私鑰對
  • http2.NextProtoTLS:NextProtoTLS是談判期間的NPN/ALPN協議,用於HTTP/2的TLS設置
  • tls.Certificate:返回一個或多個證書,實質我們解析PEM調用的X509KeyPair的函數聲明就是func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error),返回值就是Certificate

總的來說該函數是用於處理從證書憑證文件(PEM),最終獲取tls.Config作為HTTP2的使用參數

3、修改server目錄下的server.go文件,該文件是我們服務裡的核心文件,寫入內容:

grpc-gateway 系列2:Hello World

server流程剖析

我們將這一大塊代碼,分成以下幾個部分來理解

一、啟動監聽

net.Listen("tcp", EndPoint)用於監聽本地的網絡地址通知,它的函數原型func Listen(network, address string) (Listener, error)

參數:network必須傳入tcp、tcp4、tcp6、unix、unixpacket,若address為空或為0則會自動選擇一個端口號 返回值:通過查看源碼我們可以得知其返回值為Listener,結構體原型:

type Listener interface {
Accept() (Conn, error)
Close() error
Addr() Addr
}

通過分析得知,最後net.Listen會返回一個監聽器的結構體,返回給接下來的動作,讓其執行下一步的操作,它可以執行三類操作

  • Accept:接受等待並將下一個連接返回給Listener
  • Close:關閉Listener
  • Addr:返回Listener的網絡地址

二、獲取TLS

通過util.GetTLSConfig解析得到tls.Config,傳達給http.Server服務的TLSConfig配置項使用

三、創建內部服務

createInternalServer函數,是整個服務端的核心流轉部分

程序採用的是HTT2、HTTPS也就是需要支持TLS,因此在啟動grpc.NewServer前,我們要將認證的中間件註冊進去

而前面所獲取的tlsConfig僅能給HTTP使用,因此第一步我們要創建grpc的TLS認證憑證

1、創建grpc的TLS認證憑證

新增引用google.golang.org/grpc/credentials的第三方包,它實現了grpc庫支持的各種憑證,該憑證封裝了客戶機需要的所有狀態,以便與服務器進行身份驗證並進行各種斷言,例如關於客戶機的身份,角色或是否授權進行特定的呼叫

我們調用NewServerTLSFromFile來達到我們的目的,它能夠從輸入證書文件和服務器的密鑰文件構造TLS證書憑證

func NewServerTLSFromFile(certFile, keyFile string) (TransportCredentials, error) {
//LoadX509KeyPair讀取並解析來自一對文件的公鑰/私鑰對
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, err
}
//NewTLS使用tls.Config來構建基於TLS的TransportCredentials
return NewTLS(&tls.Config{Certificates: []tls.Certificate{cert}}), nil
}

2、設置grpc ServerOption

以grpc.Creds(creds)為例,其原型為func Creds(c credentials.TransportCredentials) ServerOption,該函數返回ServerOption,它為服務器連接設置憑據

3、創建grpc服務端

函數原型:

func NewServer(opt ...ServerOption) *Server

我們在此處創建了一個沒有註冊服務的grpc服務端,還沒有開始接受請求

grpcServer := grpc.NewServer(opts...)

4、註冊grpc服務

pb.RegisterHelloWorldServer(grpcServer, NewHelloService())

5、創建grpc-gateway關聯組件

ctx := context.Background()
dcreds, err := credentials.NewClientTLSFromFile(CertPemPath, CertName)
if err != nil {
log.Println("Failed to create client TLS credentials %v", err)
}
dopts := []grpc.DialOption{grpc.WithTransportCredentials(dcreds)}
  • context.Background:返回一個非空的空上下文。它沒有被註銷,沒有值,沒有過期時間。它通常由主函數、初始化和測試使用,並作為傳入請求的頂級上下文
  • credentials.NewClientTLSFromFile:從客戶機的輸入證書文件構造TLS憑證
  • grpc.WithTransportCredentials:配置一個連接級別的安全憑據(例:TLS、SSL),返回值為type DialOption
  • grpc.DialOption:DialOption選項配置我們如何設置連接(其內部具體由多個的DialOption組成,決定其設置連接的內容)

6、創建HTTP NewServeMux及註冊grpc-gateway邏輯

gwmux := runtime.NewServeMux()
// register grpc-gateway pb
if err := pb.RegisterHelloWorldHandlerFromEndpoint(ctx, gwmux, EndPoint, dopts); err != nil {
log.Println("Failed to register gw server: %v\\n", err)
}
// http服務
mux := http.NewServeMux()
mux.Handle("/", gwmux)
  • runtime.NewServeMux:返回一個新的ServeMux,它的內部映射是空的;ServeMux是grpc-gateway的一個請求多路複用器。它將http請求與模式匹配,並調用相應的處理程序
  • RegisterHelloWorldHandlerFromEndpoint:如函數名,註冊HelloWorld服務的HTTP Handle到grpc端點
  • http.NewServeMux:分配並返回一個新的ServeMux
  • mux.Handle:為給定模式註冊處理程序

(帶著疑問去看程序)為什麼gwmux可以放入mux.Handle中?

首先我們看看它們的原型是怎麼樣的

(1)http.NewServeMux()

func NewServeMux() *ServeMux {
return new(ServeMux)
}
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}

(2)runtime.NewServeMux?

func NewServeMux(opts ...ServeMuxOption) *ServeMux {
serveMux := &ServeMux{
handlers: make(map[string][]handler),
forwardResponseOptions: make([]func(context.Context, http.ResponseWriter, proto.Message) error, 0),
marshalers: makeMarshalerMIMERegistry(),
}
...
return serveMux
}

(3)http.NewServeMux()的Handle方法

func (mux *ServeMux) Handle(pattern string, handler Handler)

通過分析可得知,兩者NewServeMux都是最終返回serveMux,Handler中導出的方法僅有ServeHTTP,功能是用於響應HTTP請求

我們回到Handle interface中,可以得出結論就是任何結構體,只要實現了ServeHTTP方法,這個結構就可以稱為Handle,ServeMux會使用該Handler調用ServeHTTP方法處理請求,這也就是自定義Handler

而我們這裡正是將grpc-gateway中註冊好的HTTP Handler無縫的植入到net/http的Handle方法中

補充:在go中任何結構體只要實現了與接口相同的方法,就等同於實現了接口

7、註冊具體服務

if err := pb.RegisterHelloWorldHandlerFromEndpoint(ctx, gwmux, EndPoint, dopts); err != nil {
log.Println("Failed to register gw server: %v\\n", err)
}

在這段代碼中,我們利用了前幾小節的

  • 上下文
  • gateway-grpc的請求多路複用器
  • 服務網絡地址
  • 配置好的安全憑據

註冊了HelloWorld這一個服務

四、創建tls.NewListener

func NewListener(inner net.Listener, config *Config) net.Listener {
l := new(listener)
l.Listener = inner
l.config = config
return l
}

NewListener將會創建一個Listener,它接受兩個參數,第一個是來自內部Listener的監聽器,第二個參數是tls.Config(必須包含至少一個證書)

五、服務開始接受請求

在最後我們調用srv.Serve(tls.NewListener(conn, tlsConfig)),可以得知它是http.Server的方法,並且需要一個Listener作為參數,那麼Serve內部做了些什麼事呢?

func (srv *Server) Serve(l net.Listener) error {
defer l.Close()
...
baseCtx := context.Background() // base is always background, per Issue 16220
ctx := context.WithValue(baseCtx, ServerContextKey, srv)
for {
rw, e := l.Accept()
...
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(ctx)
}
}

粗略的看,它創建了一個context.Background()上下文對象,並調用Listener的Accept方法開始接受外部請求,在獲取到連接數據後使用newConn創建連接對象,在最後使用goroutine的方式處理連接請求,達到其目的

補充:對於HTTP/2支持,在調用Serve之前,應將srv.TLSConfig初始化為提供的Listener的TLS配置。如果srv.TLSConfig非零,並且在Config.NextProtos中不包含字符串h2,則不啟用HTTP/2支持

六、驗證功能

編寫測試客戶端

在grpc-hello-world/下新建目錄client,新建client.go文件,新增內容:

package main
import (
"log"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
pb "grpc-hello-world/proto"
)
func main() {
creds, err := credentials.NewClientTLSFromFile("../certs/server.pem", "dev")
if err != nil {
log.Println("Failed to create TLS credentials %v", err)
}
conn, err := grpc.Dial(":50052", grpc.WithTransportCredentials(creds))
defer conn.Close()
if err != nil {
log.Println(err)
}
c := pb.NewHelloWorldClient(conn)
context := context.Background()
body := &pb.HelloWorldRequest{
Referer : "Grpc",
}
r, err := c.SayHelloWorld(context, body)
if err != nil {
log.Println(err)
}
log.Println(r.Message)
}

由於客戶端只是展示測試用,就簡單的來了,原本它理應歸類到cobra的管控下,配置管理等等都應可控化

在看這篇文章的你,可以試試將測試客戶端歸類好

啟動服務端

回到grpc-hello-world/目錄下,啟動服務端go run main.go server,成功則僅返回

2018/02/26 17:19:36 gRPC and https listen on: 50052

執行測試客戶端

回到client目錄下,啟動客戶端go run client.go,成功則返回

2018/02/26 17:22:57 Grpc

執行測試Restful Api

curl -X POST -k https://localhost:50052/hello_world -d '{"referer": "restful_api"}'

成功則返回{"message":"restful_api"}


最終目錄結構

grpc-hello-world
├── certs
│ ├── server.key
│ └── server.pem
├── client
│ └── client.go
├── cmd
│ ├── root.go
│ └── server.go
├── main.go
├── pkg
│ └── util
│ ├── grpc.go
│ └── tls.go
├── proto
│ ├── google
│ │ └── api
│ │ ├── annotations.pb.go
│ │ ├── annotations.proto
│ │ ├── http.pb.go
│ │ └── http.proto
│ ├── hello.pb.go
│ ├── hello.pb.gw.go
│ └── hello.proto
└── server
├── hello.go
└── server.go

至此本節就結束了,推薦一下jergoo的文章,大家有時間可以看看

另外本節涉及了許多組件間的知識,值得大家細細的回味,非常有意義!

參考

示例代碼

  • grpc-hello-world

本文作者:煎魚,原創授權發佈

"

相關推薦

推薦中...