├── .gitignore
├── archive
├── docs
│ ├── .nojekyll
│ ├── c4
│ │ ├── php.md
│ │ ├── java.md
│ │ ├── python.md
│ │ └── nodejs.md
│ ├── c2
│ │ ├── stream.md
│ │ ├── conn-manage.md
│ │ ├── load-balancer.md
│ │ ├── trace.md
│ │ ├── hello-grpc.md
│ │ └── interceptor.md
│ ├── c3
│ │ └── middleware.md
│ ├── .DS_Store
│ ├── _media
│ │ ├── favicon.ico
│ │ ├── logo-grpc.png
│ │ ├── logo-gopher.png
│ │ ├── grpc_rest_gateway.png
│ │ ├── grpc_trace_events.jpg
│ │ ├── grpc_trace_requests.jpg
│ │ ├── grpc_concept_diagram_00.png
│ │ ├── icon.svg
│ │ └── bar.svg
│ ├── _coverpage.md
│ ├── _sidebar.md
│ ├── reference.md
│ ├── README.md
│ ├── c1
│ │ ├── install.md
│ │ └── protobuf-go.md
│ └── index.html
└── src
│ ├── .DS_Store
│ ├── hello
│ ├── .DS_Store
│ ├── client
│ │ └── main.go
│ └── server
│ │ └── main.go
│ ├── proto
│ ├── .DS_Store
│ ├── hello
│ │ ├── hello.proto
│ │ └── hello.pb.go
│ ├── compile.sh
│ ├── hello_http
│ │ ├── hello_http.proto
│ │ └── hello_http.pb.gw.go
│ ├── README.md
│ ├── test
│ │ └── test.proto
│ └── google
│ │ └── api
│ │ ├── annotations.proto
│ │ └── annotations.pb.go
│ ├── hello_http
│ ├── .DS_Store
│ ├── server_http
│ │ └── main.go
│ ├── client
│ │ └── main.go
│ └── server
│ │ └── main.go
│ ├── hello_tls
│ ├── .DS_Store
│ ├── client
│ │ └── main.go
│ └── server
│ │ └── main.go
│ ├── hello_http_2
│ ├── .DS_Store
│ ├── client
│ │ └── main.go
│ └── server
│ │ └── main.go
│ ├── hello_token
│ ├── .DS_Store
│ ├── client
│ │ └── main.go
│ └── server
│ │ └── main.go
│ ├── hello_trace
│ ├── .DS_Store
│ ├── client
│ │ └── main.go
│ └── server
│ │ └── main.go
│ ├── hello_interceptor
│ ├── .DS_Store
│ ├── client
│ │ └── main.go
│ └── server
│ │ └── main.go
│ ├── go.mod
│ └── keys
│ ├── server.key
│ └── server.pem
├── src
├── docs
│ ├── advance
│ │ ├── monitor
│ │ │ ├── index.md
│ │ │ ├── log.md
│ │ │ ├── metrics.md
│ │ │ └── tracing.md
│ │ ├── index.md
│ │ ├── metadata.md
│ │ └── auth.md
│ ├── assets
│ │ ├── favicon.ico
│ │ ├── logo-grpc.png
│ │ ├── logo-gopher.png
│ │ ├── grpc_rest_gateway.png
│ │ ├── grpc_trace_events.jpg
│ │ ├── grpc_trace_requests.jpg
│ │ ├── grpc_concept_diagram_00.png
│ │ ├── icon.svg
│ │ └── bar.svg
│ ├── ecosystem
│ │ ├── grpcurl.md
│ │ ├── middleware.md
│ │ └── index.md
│ ├── SUMMARY.md
│ ├── reference.md
│ ├── basic
│ │ ├── index.md
│ │ ├── hello-grpc.md
│ │ └── stream.md
│ └── preface.md
├── auth
│ ├── client_test.go
│ ├── keys
│ │ ├── gen.sh
│ │ ├── server.crt
│ │ └── server.key
│ ├── client.go
│ └── server.go
├── go.mod
├── metadata
│ ├── client_test.go
│ ├── server.go
│ └── client.go
├── protos
│ ├── ping
│ │ └── ping.proto
│ ├── google
│ │ ├── api
│ │ │ ├── annotations.proto
│ │ │ └── annotations.pb.go
│ │ └── protobuf
│ │ │ ├── source_context.proto
│ │ │ ├── empty.proto
│ │ │ ├── struct.proto
│ │ │ ├── wrappers.proto
│ │ │ └── duration.proto
│ └── example
│ │ └── example.proto
├── ping
│ ├── client_test.go
│ ├── server.go
│ └── client.go
├── interceptor
│ ├── client_test.go
│ ├── server.go
│ └── client.go
└── go.sum
├── docs
├── .nojekyll
├── favicon.png
├── assets
│ ├── favicon.ico
│ ├── logo-gopher.png
│ ├── logo-grpc.png
│ ├── grpc_rest_gateway.png
│ ├── grpc_trace_events.jpg
│ ├── grpc_trace_requests.jpg
│ ├── grpc_concept_diagram_00.png
│ ├── icon.svg
│ └── bar.svg
├── FontAwesome
│ └── fonts
│ │ ├── FontAwesome.ttf
│ │ ├── fontawesome-webfont.eot
│ │ ├── fontawesome-webfont.ttf
│ │ ├── fontawesome-webfont.woff
│ │ └── fontawesome-webfont.woff2
├── fonts
│ ├── open-sans-v17-all-charsets-300.woff2
│ ├── open-sans-v17-all-charsets-600.woff2
│ ├── open-sans-v17-all-charsets-700.woff2
│ ├── open-sans-v17-all-charsets-800.woff2
│ ├── open-sans-v17-all-charsets-italic.woff2
│ ├── open-sans-v17-all-charsets-regular.woff2
│ ├── open-sans-v17-all-charsets-300italic.woff2
│ ├── open-sans-v17-all-charsets-600italic.woff2
│ ├── open-sans-v17-all-charsets-700italic.woff2
│ ├── open-sans-v17-all-charsets-800italic.woff2
│ ├── source-code-pro-v11-all-charsets-500.woff2
│ ├── fonts.css
│ └── SOURCE-CODE-PRO-LICENSE.txt
├── css
│ ├── print.css
│ └── general.css
├── ayu-highlight.css
├── highlight.css
├── theme
│ ├── pagetoc.css
│ └── pagetoc.js
├── favicon.svg
└── tomorrow-night.css
├── book.toml
├── README.md
├── LICENSE
└── theme
├── pagetoc.css
└── pagetoc.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 |
--------------------------------------------------------------------------------
/archive/docs/.nojekyll:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/archive/docs/c4/php.md:
--------------------------------------------------------------------------------
1 | # PHP
2 |
--------------------------------------------------------------------------------
/archive/docs/c2/stream.md:
--------------------------------------------------------------------------------
1 | # 流式调用
2 |
--------------------------------------------------------------------------------
/archive/docs/c4/java.md:
--------------------------------------------------------------------------------
1 | # Java
2 |
--------------------------------------------------------------------------------
/archive/docs/c4/python.md:
--------------------------------------------------------------------------------
1 | # Python
2 |
--------------------------------------------------------------------------------
/archive/docs/c3/middleware.md:
--------------------------------------------------------------------------------
1 | # 中间件
2 |
--------------------------------------------------------------------------------
/archive/docs/c4/nodejs.md:
--------------------------------------------------------------------------------
1 | # node.js
2 |
--------------------------------------------------------------------------------
/archive/docs/c2/conn-manage.md:
--------------------------------------------------------------------------------
1 | # 连接管理
2 |
3 |
--------------------------------------------------------------------------------
/archive/docs/c2/load-balancer.md:
--------------------------------------------------------------------------------
1 | # 负载均衡(load-balancer)
2 |
--------------------------------------------------------------------------------
/src/docs/advance/monitor/index.md:
--------------------------------------------------------------------------------
1 | # 监控
2 |
3 | ---
4 |
--------------------------------------------------------------------------------
/src/docs/advance/monitor/log.md:
--------------------------------------------------------------------------------
1 | # log
2 |
3 | ---
4 |
--------------------------------------------------------------------------------
/src/docs/advance/monitor/metrics.md:
--------------------------------------------------------------------------------
1 | # metrics
2 |
3 | ---
4 |
5 |
--------------------------------------------------------------------------------
/src/docs/advance/index.md:
--------------------------------------------------------------------------------
1 | # 进阶
2 |
3 | ---
4 | 这个部分会介绍一些 gRPC 的高级功能特性。
5 |
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
1 | This file makes sure that Github Pages doesn't process mdBook's output.
2 |
--------------------------------------------------------------------------------
/docs/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/docs/favicon.png
--------------------------------------------------------------------------------
/archive/docs/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/archive/docs/.DS_Store
--------------------------------------------------------------------------------
/archive/src/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/archive/src/.DS_Store
--------------------------------------------------------------------------------
/docs/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/docs/assets/favicon.ico
--------------------------------------------------------------------------------
/archive/src/hello/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/archive/src/hello/.DS_Store
--------------------------------------------------------------------------------
/archive/src/proto/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/archive/src/proto/.DS_Store
--------------------------------------------------------------------------------
/docs/assets/logo-gopher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/docs/assets/logo-gopher.png
--------------------------------------------------------------------------------
/docs/assets/logo-grpc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/docs/assets/logo-grpc.png
--------------------------------------------------------------------------------
/src/docs/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/src/docs/assets/favicon.ico
--------------------------------------------------------------------------------
/src/docs/assets/logo-grpc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/src/docs/assets/logo-grpc.png
--------------------------------------------------------------------------------
/archive/docs/_media/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/archive/docs/_media/favicon.ico
--------------------------------------------------------------------------------
/archive/src/hello_http/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/archive/src/hello_http/.DS_Store
--------------------------------------------------------------------------------
/archive/src/hello_tls/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/archive/src/hello_tls/.DS_Store
--------------------------------------------------------------------------------
/src/docs/assets/logo-gopher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/src/docs/assets/logo-gopher.png
--------------------------------------------------------------------------------
/src/docs/ecosystem/grpcurl.md:
--------------------------------------------------------------------------------
1 | # grpcurl
2 |
3 | ---
4 |
5 | > 项目地址:[grpcurl](https://github.com/fullstorydev/grpcurl)
6 |
--------------------------------------------------------------------------------
/archive/docs/_media/logo-grpc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/archive/docs/_media/logo-grpc.png
--------------------------------------------------------------------------------
/archive/src/hello_http_2/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/archive/src/hello_http_2/.DS_Store
--------------------------------------------------------------------------------
/archive/src/hello_token/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/archive/src/hello_token/.DS_Store
--------------------------------------------------------------------------------
/archive/src/hello_trace/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/archive/src/hello_trace/.DS_Store
--------------------------------------------------------------------------------
/docs/assets/grpc_rest_gateway.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/docs/assets/grpc_rest_gateway.png
--------------------------------------------------------------------------------
/docs/assets/grpc_trace_events.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/docs/assets/grpc_trace_events.jpg
--------------------------------------------------------------------------------
/archive/docs/_media/logo-gopher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/archive/docs/_media/logo-gopher.png
--------------------------------------------------------------------------------
/docs/assets/grpc_trace_requests.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/docs/assets/grpc_trace_requests.jpg
--------------------------------------------------------------------------------
/src/docs/assets/grpc_rest_gateway.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/src/docs/assets/grpc_rest_gateway.png
--------------------------------------------------------------------------------
/src/docs/assets/grpc_trace_events.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/src/docs/assets/grpc_trace_events.jpg
--------------------------------------------------------------------------------
/archive/src/hello_interceptor/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/archive/src/hello_interceptor/.DS_Store
--------------------------------------------------------------------------------
/docs/FontAwesome/fonts/FontAwesome.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/docs/FontAwesome/fonts/FontAwesome.ttf
--------------------------------------------------------------------------------
/docs/assets/grpc_concept_diagram_00.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/docs/assets/grpc_concept_diagram_00.png
--------------------------------------------------------------------------------
/src/docs/assets/grpc_trace_requests.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/src/docs/assets/grpc_trace_requests.jpg
--------------------------------------------------------------------------------
/archive/docs/_media/grpc_rest_gateway.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/archive/docs/_media/grpc_rest_gateway.png
--------------------------------------------------------------------------------
/archive/docs/_media/grpc_trace_events.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/archive/docs/_media/grpc_trace_events.jpg
--------------------------------------------------------------------------------
/archive/docs/_media/grpc_trace_requests.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/archive/docs/_media/grpc_trace_requests.jpg
--------------------------------------------------------------------------------
/src/docs/assets/grpc_concept_diagram_00.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/src/docs/assets/grpc_concept_diagram_00.png
--------------------------------------------------------------------------------
/archive/docs/_media/grpc_concept_diagram_00.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/archive/docs/_media/grpc_concept_diagram_00.png
--------------------------------------------------------------------------------
/docs/FontAwesome/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/docs/FontAwesome/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/docs/FontAwesome/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/docs/FontAwesome/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/docs/FontAwesome/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/docs/FontAwesome/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/docs/fonts/open-sans-v17-all-charsets-300.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/docs/fonts/open-sans-v17-all-charsets-300.woff2
--------------------------------------------------------------------------------
/docs/fonts/open-sans-v17-all-charsets-600.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/docs/fonts/open-sans-v17-all-charsets-600.woff2
--------------------------------------------------------------------------------
/docs/fonts/open-sans-v17-all-charsets-700.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/docs/fonts/open-sans-v17-all-charsets-700.woff2
--------------------------------------------------------------------------------
/docs/fonts/open-sans-v17-all-charsets-800.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/docs/fonts/open-sans-v17-all-charsets-800.woff2
--------------------------------------------------------------------------------
/docs/FontAwesome/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/docs/FontAwesome/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/src/docs/ecosystem/middleware.md:
--------------------------------------------------------------------------------
1 | # gRPC Middleware
2 |
3 | ---
4 |
5 | > 项目地址:[go-grpc-middleware](https://github.com/grpc-ecosystem/go-grpc-middleware)
6 |
--------------------------------------------------------------------------------
/docs/fonts/open-sans-v17-all-charsets-italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/docs/fonts/open-sans-v17-all-charsets-italic.woff2
--------------------------------------------------------------------------------
/docs/fonts/open-sans-v17-all-charsets-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/docs/fonts/open-sans-v17-all-charsets-regular.woff2
--------------------------------------------------------------------------------
/docs/fonts/open-sans-v17-all-charsets-300italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/docs/fonts/open-sans-v17-all-charsets-300italic.woff2
--------------------------------------------------------------------------------
/docs/fonts/open-sans-v17-all-charsets-600italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/docs/fonts/open-sans-v17-all-charsets-600italic.woff2
--------------------------------------------------------------------------------
/docs/fonts/open-sans-v17-all-charsets-700italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/docs/fonts/open-sans-v17-all-charsets-700italic.woff2
--------------------------------------------------------------------------------
/docs/fonts/open-sans-v17-all-charsets-800italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/docs/fonts/open-sans-v17-all-charsets-800italic.woff2
--------------------------------------------------------------------------------
/docs/fonts/source-code-pro-v11-all-charsets-500.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jergoo/go-grpc-tutorial/HEAD/docs/fonts/source-code-pro-v11-all-charsets-500.woff2
--------------------------------------------------------------------------------
/src/docs/ecosystem/index.md:
--------------------------------------------------------------------------------
1 | # gRPC 生态
2 |
3 | ---
4 |
5 | gRPC 的发展非常迅速,从 gRPC 扩展出来的生态包含了各种各样的工具,这部分介绍几个常用的工具。
6 |
7 | > 项目地址:[grpc-ecosystem](https://github.com/grpc-ecosystem)
8 |
--------------------------------------------------------------------------------
/archive/src/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/jergoo/go-grpc-example
2 |
3 | go 1.14
4 |
5 | require (
6 | github.com/golang/protobuf v1.4.2
7 | github.com/grpc-ecosystem/grpc-gateway v1.14.6
8 | golang.org/x/net v0.0.0-20200625001655-4c5254603344
9 | google.golang.org/grpc v1.30.0
10 | )
11 |
--------------------------------------------------------------------------------
/src/auth/client_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestPing(t *testing.T) {
8 | tests := []struct {
9 | name string
10 | }{
11 | {name: "ping"},
12 | }
13 | for _, tt := range tests {
14 | t.Run(tt.name, func(t *testing.T) {
15 | Ping()
16 | })
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/auth/keys/gen.sh:
--------------------------------------------------------------------------------
1 | #! bin/sh
2 |
3 | openssl req -newkey rsa:2048 -x509 -nodes -sha256 -days 3650 \
4 | -keyout server.key -new -out server.crt \
5 | -subj /CN=grpc.server -reqexts SAN -extensions SAN \
6 | -config <(cat /System/Library/OpenSSL/openssl.cnf \
7 | <(printf '[SAN]\nsubjectAltName=DNS:grpc.server'))
8 |
9 |
--------------------------------------------------------------------------------
/book.toml:
--------------------------------------------------------------------------------
1 | [book]
2 | title = "Go gRPC 简明指南"
3 | authors = ["jergoo"]
4 | description = "Go gRPC Tutorial"
5 | language = "zh"
6 | multilingual = false
7 | src = "src/docs"
8 |
9 | [build]
10 | build-dir = "docs"
11 |
12 | [preprocessor.pagetoc]
13 | [output.html]
14 | additional-css = ["theme/pagetoc.css"]
15 | additional-js = ["theme/pagetoc.js"]
16 |
--------------------------------------------------------------------------------
/archive/src/keys/server.key:
--------------------------------------------------------------------------------
1 | -----BEGIN EC PARAMETERS-----
2 | BgUrgQQAIg==
3 | -----END EC PARAMETERS-----
4 | -----BEGIN EC PRIVATE KEY-----
5 | MIGkAgEBBDCGtG0mVhqS2S+XmFnqBo/gHPH6TPxw0C9AujL/I7+IFldRoRuaFX5u
6 | Z2PyqzDcqZmgBwYFK4EEACKhZANiAAQ9FD9C+8rnCfPW4hy/KRFOeYuXvH5TBc4t
7 | tqvKUAsmpnQVYuxYpr5k2p+w1sRN0l8B+uMmFXzmB1wCSiGwOrCEEh2wFao7zeeS
8 | 0KFc7jZpy6Lkch6eS6DK7NayrdENwLw=
9 | -----END EC PRIVATE KEY-----
10 |
--------------------------------------------------------------------------------
/src/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/jergoo/go-grpc-tutorial
2 |
3 | go 1.19
4 |
5 | require (
6 | github.com/golang/protobuf v1.5.2
7 | google.golang.org/grpc v1.51.0
8 | google.golang.org/protobuf v1.28.1
9 | )
10 |
11 | require (
12 | golang.org/x/net v0.2.0 // indirect
13 | golang.org/x/sys v0.2.0 // indirect
14 | golang.org/x/text v0.4.0 // indirect
15 | google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect
16 | )
17 |
--------------------------------------------------------------------------------
/archive/src/proto/hello/hello.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3"; // 指定proto版本
2 | package hello; // 指定包名
3 |
4 | option go_package = "hello";
5 |
6 | // 定义Hello服务
7 | service Hello {
8 | // 定义SayHello方法
9 | rpc SayHello(HelloRequest) returns (HelloResponse) {}
10 | }
11 |
12 | // HelloRequest 请求结构
13 | message HelloRequest {
14 | string name = 1;
15 | }
16 |
17 | // HelloResponse 响应结构
18 | message HelloResponse {
19 | string message = 1;
20 | }
21 |
--------------------------------------------------------------------------------
/archive/docs/_coverpage.md:
--------------------------------------------------------------------------------
1 | 
2 | 
3 |
4 |
5 |
6 |
7 |
8 |
Go gRPC Tutorial
9 |
10 |
11 | - 🚚_coding choo-choo_🚌🚌🚌🚌🚌🚌🚌
12 |
13 |
14 | [GitHub](https://github.com/jergoo/go-grpc-tutorial)
15 | [开始学习](#gRPC)
16 |
17 | 
18 |
--------------------------------------------------------------------------------
/src/metadata/client_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestPing(t *testing.T) {
8 | tests := []struct {
9 | name string
10 | }{
11 | {name: "ping"},
12 | }
13 | for _, tt := range tests {
14 | t.Run(tt.name, func(t *testing.T) {
15 | Ping()
16 | })
17 | }
18 | }
19 |
20 | func TestMultiPong(t *testing.T) {
21 | tests := []struct {
22 | name string
23 | }{
24 | {name: "multi pong"},
25 | }
26 | for _, tt := range tests {
27 | t.Run(tt.name, func(t *testing.T) {
28 | MultiPong()
29 | })
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/archive/docs/_sidebar.md:
--------------------------------------------------------------------------------
1 | * **入门**
2 | * [安装](c1/install.md "grpc install")
3 | * [Protobuf语法](c1/protobuf.md)
4 | * [Protobuf⇢Go转换](c1/protobuf-go.md)
5 | * **实践**
6 | * [Hello gRPC](c2/hello-grpc.md)
7 | * [认证](c2/auth.md)
8 | * [拦截器](c2/interceptor.md)
9 | * [流式传输](c2/stream.md)
10 | * [内置Trace](c2/trace.md)
11 | * [负载均衡](c2/load-balancer.md)
12 | * **进阶**
13 | * [Http网关](c3/gateway.md)
14 | * **多语言**
15 | * [Java](c4/java.md)
16 | * [PHP](c4/php.md)
17 | * [Python](c4/python.md)
18 | * [Node.js](c4/nodejs.md)
19 | * [**参考**](reference.md)
20 |
--------------------------------------------------------------------------------
/src/docs/SUMMARY.md:
--------------------------------------------------------------------------------
1 | # Summary
2 |
3 | [前言](./preface.md)
4 |
5 | ---
6 |
7 | - [入门](./basic/index.md)
8 | - [Hello gRPC](./basic/hello-grpc.md)
9 | - [gRPC 流](./basic/stream.md)
10 | - [Protobuf](./basic/protobuf.md)
11 |
12 | - [进阶](./advance/index.md)
13 | - [拦截器](./advance/interceptor.md)
14 | - [metadata](./advance/metadata.md)
15 | - [安全认证](./advance/auth.md)
16 |
17 | - [生态](./ecosystem/index.md)
18 | - [gRPC Gateway](./ecosystem/gateway.md)
19 | - [gRPC Middleware](./ecosystem/middleware.md)
20 | - [grpcurl](./ecosystem/grpcurl.md)
21 |
22 | ---
23 |
24 | [参考](./reference.md)
25 |
--------------------------------------------------------------------------------
/archive/docs/reference.md:
--------------------------------------------------------------------------------
1 | # 参考
2 |
3 | ## 相关资料:
4 |
5 | * [grpc官网](https://grpc.io/)
6 | * [grpc中文文档](http://doc.oschina.net/grpc)
7 | * [protobuf官方文档](https://developers.google.com/protocol-buffers/)
8 |
9 |
10 | ## 相关项目
11 |
12 | * [grpc/grpc](https://github.com/grpc/grpc)
13 | * [grpc-go](https://github.com/grpc/grpc-go/)
14 | * [grpc-java](https://github.com/grpc/grpc-java)
15 | * [google/protobuf](https://github.com/google/protobuf)
16 | * [golang/protobuf](https://github.com/golang/protobuf)
17 | * [grpc-middleware](https://github.com/grpc-ecosystem/go-grpc-middleware)
18 | * [grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway)
19 |
--------------------------------------------------------------------------------
/archive/src/proto/compile.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | # 编译test.proto
4 | protoc -I . --go_out=plugins=grpc:. test/test.proto
5 |
6 | # 编译hello.proto
7 | protoc -I . --go_out=plugins=grpc:. hello/hello.proto
8 |
9 | # 编译google api,新版编译器可以省略M参数
10 | protoc -I . --go_out=plugins=grpc,Mgoogle/protobuf/descriptor.proto=github.com/golang/protobuf/protoc-gen-go/descriptor:. google/api/*.proto
11 |
12 | # 编译hello_http.proto
13 | protoc -I . --go_out=plugins=grpc,Mgoogle/api/annotations.proto=github.com/jergoo/go-grpc-example/proto/google/api:. hello_http/*.proto
14 |
15 | # 编译hello_http.proto gateway
16 | protoc --grpc-gateway_out=logtostderr=true:. hello_http/hello_http.proto
17 |
--------------------------------------------------------------------------------
/archive/src/proto/hello_http/hello_http.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package hello_http;
4 | option go_package = "hello_http";
5 |
6 | import "google/api/annotations.proto";
7 |
8 | // 定义Hello服务
9 | service HelloHTTP {
10 | // 定义SayHello方法
11 | rpc SayHello(HelloHTTPRequest) returns (HelloHTTPResponse) {
12 | // http option
13 | option (google.api.http) = {
14 | post: "/example/echo"
15 | body: "*"
16 | };
17 | }
18 | }
19 |
20 | // HelloRequest 请求结构
21 | message HelloHTTPRequest {
22 | string name = 1;
23 | }
24 |
25 | // HelloResponse 响应结构
26 | message HelloHTTPResponse {
27 | string message = 1;
28 | }
29 |
--------------------------------------------------------------------------------
/src/protos/ping/ping.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3"; // 指定proto版本
2 | package protos; // 指定包名
3 |
4 | // 指定go包路径
5 | option go_package = "protos/ping";
6 |
7 | // 定义PingPong服务
8 | service PingPong {
9 | // 单次请求-响应模式
10 | rpc Ping(PingRequest) returns (PongResponse);
11 | // 服务端流模式
12 | rpc MultiPong(PingRequest) returns (stream PongResponse);
13 | // 客户端流模式
14 | rpc MultiPing(stream PingRequest) returns (PongResponse);
15 | // 双向流模式
16 | rpc MultiPingPong(stream PingRequest) returns (stream PongResponse);
17 | }
18 |
19 | // PingRequest 请求结构
20 | message PingRequest {
21 | string value = 1;
22 | }
23 |
24 | // PongResponse 响应结构
25 | message PongResponse {
26 | string value = 1;
27 | }
28 |
--------------------------------------------------------------------------------
/src/docs/reference.md:
--------------------------------------------------------------------------------
1 | # 参考
2 |
3 | ---
4 |
5 | > **Doc & Book**
6 | >
7 | > * [gRPC website](https://grpc.io/)
8 | > * [protobuf website](https://developers.google.com/protocol-buffers)
9 | > * [gRPC: Up and Running](https://www.oreilly.com/library/view/grpc-up-and/9781492058328/)
10 |
11 | > **Projects**
12 | >
13 | > * [grpc/grpc](https://github.com/grpc/grpc)
14 | > * [grpc-go](https://github.com/grpc/grpc-go/)
15 | > * [google/protobuf](https://github.com/google/protobuf)
16 | > * [golang/protobuf](https://github.com/golang/protobuf)
17 | > * [grpc-middleware](https://github.com/grpc-ecosystem/go-grpc-middleware)
18 | > * [grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway)
19 | > * [grpcurl](https://github.com/fullstorydev/grpcurl)
20 | > * [buf](https://buf.build/)
21 |
--------------------------------------------------------------------------------
/archive/src/hello/client/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | pb "github.com/jergoo/go-grpc-example/proto/hello" // 引入proto包
5 |
6 | "golang.org/x/net/context"
7 | "google.golang.org/grpc"
8 | "google.golang.org/grpc/grpclog"
9 | )
10 |
11 | const (
12 | // Address gRPC服务地址
13 | Address = "127.0.0.1:50052"
14 | )
15 |
16 | func main() {
17 | // 连接
18 | conn, err := grpc.Dial(Address, grpc.WithInsecure())
19 | if err != nil {
20 | grpclog.Fatalln(err)
21 | }
22 | defer conn.Close()
23 |
24 | // 初始化客户端
25 | c := pb.NewHelloClient(conn)
26 |
27 | // 调用方法
28 | req := &pb.HelloRequest{Name: "gRPC"}
29 | res, err := c.SayHello(context.Background(), req)
30 |
31 | if err != nil {
32 | grpclog.Fatalln(err)
33 | }
34 |
35 | grpclog.Println(res.Message)
36 | }
37 |
--------------------------------------------------------------------------------
/archive/src/hello_trace/client/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | pb "github.com/jergoo/go-grpc-example/proto/hello" // 引入proto包
5 |
6 | "golang.org/x/net/context"
7 | "google.golang.org/grpc"
8 | "google.golang.org/grpc/grpclog"
9 | )
10 |
11 | const (
12 | // Address gRPC服务地址
13 | Address = "127.0.0.1:50052"
14 | )
15 |
16 | func main() {
17 | // 连接
18 | conn, err := grpc.Dial(Address, grpc.WithInsecure())
19 | if err != nil {
20 | grpclog.Fatalln(err)
21 | }
22 | defer conn.Close()
23 |
24 | // 初始化客户端
25 | c := pb.NewHelloClient(conn)
26 |
27 | // 调用方法
28 | req := &pb.HelloRequest{Name: "gRPC"}
29 | res, err := c.SayHello(context.Background(), req)
30 | if err != nil {
31 | grpclog.Fatalln(err)
32 | }
33 |
34 | grpclog.Println(res.Message)
35 | }
36 |
--------------------------------------------------------------------------------
/archive/src/proto/README.md:
--------------------------------------------------------------------------------
1 | # 编译器使用
2 |
3 | ## 使用`protoc`命令编译`.proto`文件:
4 |
5 | * -I 参数:指定import路径,可以指定多个 -I参数,按顺序查找,默认只查找当前目录
6 | * --go_out :golang编译支持,支持以下参数:
7 | * plugins=plugin1+plugin2 - 指定插件,目前只有grpc,即:plugins=grpc
8 | * M 参数 - 指定导入的.proto文件路径编译后对应的golang包名(不指定本参数默认就是.proto文件中import语句的路径)
9 | * import_prefix=xxx - 为所有import路径添加前缀,主要用于编译子目录内的多个proto文件,这个参数按理说很有用,尤其适用替代hello_http.proto编译时的M参数,但是实际使用时有个蛋疼的问题,自己尝试看看吧
10 | * import_path=foo/bar - 用于指定未声明package或go_package的文件的包名,最右面的斜线前的字符会被忽略
11 | * :编译文件路径 .proto文件路径(支持通配符)
12 | * 同一个包内包含多个.proto文件时使用通配符同时编译所有文件,单独编译每个文件会存在变量命名冲突,例如编译hello_http.proto那里所示
13 |
14 | > 完整示例:
15 |
16 | ```protobuf
17 | protoc --go_out=plugins=grpc,Mfoo/bar.proto=bar,import_prefix=foo/,import_path=foo/bar:. ./*.proto
18 | ```
19 |
--------------------------------------------------------------------------------
/src/docs/basic/index.md:
--------------------------------------------------------------------------------
1 | # 入门
2 |
3 | ---
4 |
5 | 这个部分通过创建一个简单的服务说明 go gRPC 的基本使用方法和场景,并介绍 protobuf 的基本语法。
6 |
7 | ## 环境准备
8 |
9 | ### protobuf 编译器
10 |
11 | 项目地址:[google/protobuf](https://github.com/google/protobuf)
12 |
13 | 这里使用`brew`工具安装,也可以下载编译好的可执行文件:
14 |
15 | ```sh
16 | $ brew install protobuf
17 | ```
18 |
19 | 执行`protoc`命令查看当前版本:
20 |
21 | ```sh
22 | $ protoc --version
23 | libprotoc 3.21.6
24 | ```
25 |
26 | ### go 编译插件
27 |
28 | 项目地址:
29 | * [protobuf-go](https://github.com/protocolbuffers/protobuf-go)
30 | * [grpc-go](https://github.com/grpc/grpc-go)
31 |
32 | 安装:
33 | ```sh
34 | $ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
35 | $ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
36 |
37 | $ export PATH="$PATH:$(go env GOPATH)/bin"
38 | ```
39 |
--------------------------------------------------------------------------------
/src/docs/preface.md:
--------------------------------------------------------------------------------
1 | # 前言
2 |
3 | ---
4 |
5 |
10 |
11 |
12 |

13 |

14 |
15 |
16 | [gRPC](https://grpc.io/) 是一个高性能、开源、通用的 RPC 框架,由 Google 推出,基于 [HTTP2](https://http2.github.io/) 协议标准设计开发,采用 [Protocol Buffers](https://developers.google.com/protocol-buffers/) 数据序列化协议,支持多种开发语言,广泛应用于分布式系统服务间的调用。
17 |
18 | 本项目旨在通过一些简单的示例,提供一个简明的 go gRPC 使用指南,帮助新手快速学会使用 gRPC,而非深究其底层原理,内容包含 gRPC 常用的功能特性,以及 gRPC 生态中的一些工具。
19 |
20 | > 本项目及源码地址:[go-grpc-tutorial](https://github.com/jergoo/go-grpc-tutorial)
21 |
22 |
23 |
--------------------------------------------------------------------------------
/archive/src/hello_http/server_http/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "net/http"
5 |
6 | "github.com/grpc-ecosystem/grpc-gateway/runtime"
7 | "golang.org/x/net/context"
8 | "google.golang.org/grpc"
9 | "google.golang.org/grpc/grpclog"
10 |
11 | gw "github.com/jergoo/go-grpc-example/proto/hello_http"
12 | )
13 |
14 | func main() {
15 | ctx := context.Background()
16 | ctx, cancel := context.WithCancel(ctx)
17 | defer cancel()
18 |
19 | // grpc服务地址
20 | endpoint := "127.0.0.1:50052"
21 | mux := runtime.NewServeMux()
22 | opts := []grpc.DialOption{grpc.WithInsecure()}
23 |
24 | // HTTP转grpc
25 | err := gw.RegisterHelloHTTPHandlerFromEndpoint(ctx, mux, endpoint, opts)
26 | if err != nil {
27 | grpclog.Fatalf("Register handler err:%v\n", err)
28 | }
29 |
30 | grpclog.Println("HTTP Listen on 8080")
31 | http.ListenAndServe(":8080", mux)
32 | }
33 |
--------------------------------------------------------------------------------
/archive/src/proto/test/test.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package test;
3 |
4 | option go_package="test";
5 |
6 | // TestService 测试服务
7 | service TestService {
8 | // Test 测试方法
9 | rpc Test(Request) returns (Response) {};
10 | }
11 |
12 | // Request 请求结构
13 | message Request {
14 | string name = 1;
15 | }
16 |
17 | // Response 响应结构
18 | message Response {
19 | string message = 1;
20 | }
21 |
22 | // Test 测试
23 | message Test {
24 | int32 age = 1;
25 | int64 count = 2;
26 | double money = 3;
27 | float score = 4;
28 | string name = 5;
29 | bool fat = 6;
30 | bytes char = 7;
31 | // Status 枚举状态
32 | enum Status {
33 | OK = 0;
34 | FAIL = 1;
35 | }
36 | Status status = 8;
37 | // Child 子结构
38 | message Child {
39 | string sex = 1;
40 | }
41 | Child child = 9;
42 | map dict = 10;
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/archive/src/hello_http/client/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | pb "github.com/jergoo/go-grpc-example/proto/hello_http"
5 | "golang.org/x/net/context"
6 | "google.golang.org/grpc"
7 | "google.golang.org/grpc/grpclog"
8 | )
9 |
10 | const (
11 | // Address gRPC服务地址
12 | Address = "127.0.0.1:50052"
13 | )
14 |
15 | func main() {
16 | // 连接
17 | conn, err := grpc.Dial(Address, grpc.WithInsecure())
18 |
19 | if err != nil {
20 | grpclog.Fatalln(err)
21 | }
22 |
23 | defer conn.Close()
24 |
25 | // 初始化客户端
26 | c := pb.NewHelloHTTPClient(conn)
27 |
28 | // 调用方法
29 | reqBody := new(pb.HelloHTTPRequest)
30 | reqBody.Name = "gRPC"
31 | r, err := c.SayHello(context.Background(), reqBody)
32 | if err != nil {
33 | grpclog.Fatalln(err)
34 | }
35 |
36 | grpclog.Println(r.Message)
37 | }
38 |
39 | // OR: curl -X POST -k http://localhost:8080/example/echo -d '{"name": "gRPC-HTTP is working!"}'
40 |
--------------------------------------------------------------------------------
/docs/css/print.css:
--------------------------------------------------------------------------------
1 |
2 | #sidebar,
3 | #menu-bar,
4 | .nav-chapters,
5 | .mobile-nav-chapters {
6 | display: none;
7 | }
8 |
9 | #page-wrapper.page-wrapper {
10 | transform: none;
11 | margin-left: 0px;
12 | overflow-y: initial;
13 | }
14 |
15 | #content {
16 | max-width: none;
17 | margin: 0;
18 | padding: 0;
19 | }
20 |
21 | .page {
22 | overflow-y: initial;
23 | }
24 |
25 | code {
26 | background-color: #666666;
27 | border-radius: 5px;
28 |
29 | /* Force background to be printed in Chrome */
30 | -webkit-print-color-adjust: exact;
31 | }
32 |
33 | pre > .buttons {
34 | z-index: 2;
35 | }
36 |
37 | a, a:visited, a:active, a:hover {
38 | color: #4183c4;
39 | text-decoration: none;
40 | }
41 |
42 | h1, h2, h3, h4, h5, h6 {
43 | page-break-inside: avoid;
44 | page-break-after: avoid;
45 | }
46 |
47 | pre, code {
48 | page-break-inside: avoid;
49 | white-space: pre-wrap;
50 | }
51 |
52 | .fa {
53 | display: none !important;
54 | }
55 |
--------------------------------------------------------------------------------
/archive/src/hello_tls/client/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | pb "github.com/jergoo/go-grpc-example/proto/hello" // 引入proto包
5 |
6 | "golang.org/x/net/context"
7 | "google.golang.org/grpc"
8 | "google.golang.org/grpc/credentials" // 引入grpc认证包
9 | "google.golang.org/grpc/grpclog"
10 | )
11 |
12 | const (
13 | // Address gRPC服务地址
14 | Address = "127.0.0.1:50052"
15 | )
16 |
17 | func main() {
18 | // TLS连接
19 | creds, err := credentials.NewClientTLSFromFile("../../keys/server.pem", "server name")
20 | if err != nil {
21 | grpclog.Fatalf("Failed to create TLS credentials %v", err)
22 | }
23 |
24 | conn, err := grpc.Dial(Address, grpc.WithTransportCredentials(creds))
25 | if err != nil {
26 | grpclog.Fatalln(err)
27 | }
28 | defer conn.Close()
29 |
30 | // 初始化客户端
31 | c := pb.NewHelloClient(conn)
32 |
33 | // 调用方法
34 | req := &pb.HelloRequest{Name: "gRPC"}
35 | res, err := c.SayHello(context.Background(), req)
36 | if err != nil {
37 | grpclog.Fatalln(err)
38 | }
39 |
40 | grpclog.Println(res.Message)
41 | }
42 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Go gRPC Tutorial
2 |
3 | 一份简明的 go gRPC 使用指南,包含 gRPC 常用的功能特性,以及 gRPC 生态中的常用工具。
4 |
5 | 在线阅读:[Go gRPC Tutorial](https://jergoo.github.io/go-grpc-tutorial/)
6 |
7 | 更新进度:
8 |
9 | - [x] 入门
10 | - [x] Hello gRPC
11 | - [x] gRPC 流
12 | - [x] Protobuf
13 |
14 | - [x] 进阶
15 | - [x] 拦截器
16 | - [x] metadata
17 | - [x] 安全认证
18 |
19 | - [ ] 生态
20 | - [ ] gRPC Gateway
21 | - [ ] gRPC Middleware
22 | - [ ] grpcurl
23 |
24 | ---
25 |
26 | > 历史版本:SegmentFault **Golang gRPC实践** 系列文章:
27 | > * [Golang gRPC实践 连载一 gRPC介绍与安装](https://segmentfault.com/a/1190000007880647)
28 | > * [Golang gRPC实践 连载二 Hello gRPC](https://segmentfault.com/a/1190000007909829)
29 | > * [Golang gRPC实践 连载三 Protobuf语法](https://segmentfault.com/a/1190000007917576)
30 | > * [Golang gRPC实践 连载四 gRPC认证](https://segmentfault.com/a/1190000007933303)
31 | > * [Golang gRPC实践 连载五 拦截器 Interceptor](https://segmentfault.com/a/1190000007997759)
32 | > * [Golang gRPC实践 连载六 内置Trace](https://segmentfault.com/a/1190000008087436)
33 | > * [Golang gRPC实践 连载七 http转换](https://segmentfault.com/a/1190000008106582)
34 |
--------------------------------------------------------------------------------
/src/protos/google/api/annotations.proto:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2015, Google Inc.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | syntax = "proto3";
16 |
17 | package google.api;
18 |
19 | import "google/api/http.proto";
20 | import "google/protobuf/descriptor.proto";
21 |
22 | option java_multiple_files = true;
23 | option java_outer_classname = "AnnotationsProto";
24 | option java_package = "com.google.api";
25 |
26 | extend google.protobuf.MethodOptions {
27 | // See `HttpRule`.
28 | HttpRule http = 72295728;
29 | }
30 |
--------------------------------------------------------------------------------
/src/ping/client_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestPing(t *testing.T) {
8 | tests := []struct {
9 | name string
10 | }{
11 | {name: "ping"},
12 | }
13 | for _, tt := range tests {
14 | t.Run(tt.name, func(t *testing.T) {
15 | Ping()
16 | })
17 | }
18 | }
19 |
20 | func TestMultiPong(t *testing.T) {
21 | tests := []struct {
22 | name string
23 | }{
24 | {name: "multi pong"},
25 | }
26 | for _, tt := range tests {
27 | t.Run(tt.name, func(t *testing.T) {
28 | MultiPong()
29 | })
30 | }
31 | }
32 |
33 | func TestMultiPing(t *testing.T) {
34 | tests := []struct {
35 | name string
36 | }{
37 | {name: "multi ping"},
38 | }
39 | for _, tt := range tests {
40 | t.Run(tt.name, func(t *testing.T) {
41 | MultiPing()
42 | })
43 | }
44 | }
45 |
46 | func TestMultiPingPong(t *testing.T) {
47 | tests := []struct {
48 | name string
49 | }{
50 | {name: "multi ping&pong"},
51 | }
52 | for _, tt := range tests {
53 | t.Run(tt.name, func(t *testing.T) {
54 | MultiPingPong()
55 | })
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/archive/docs/README.md:
--------------------------------------------------------------------------------
1 | # gRPC
2 |
3 | [gRPC](https://grpc.io/) 是一个高性能、开源、通用的RPC框架,由Google推出,基于[HTTP2](https://http2.github.io/)协议标准设计开发,默认采用[Protocol Buffers](https://developers.google.com/protocol-buffers/)数据序列化协议,支持多种开发语言。gRPC提供了一种简单的方法来精确的定义服务,并且为客户端和服务端自动生成可靠的功能库。
4 |
5 | 在gRPC客户端可以直接调用不同服务器上的远程程序,使用姿势看起来就像调用本地程序一样,很容易去构建分布式应用和服务。和很多RPC系统一样,服务端负责实现定义好的接口并处理客户端的请求,客户端根据接口描述直接调用需要的服务。客户端和服务端可以分别使用gRPC支持的不同语言实现。
6 |
7 | 
8 |
9 |
10 | ## 主要特性
11 |
12 | * 强大的IDL
13 |
14 | gRPC使用ProtoBuf来定义服务,ProtoBuf是由Google开发的一种数据序列化协议(类似于XML、JSON、hessian)。ProtoBuf能够将数据进行序列化,并广泛应用在数据存储、通信协议等方面。
15 |
16 | * 多语言支持
17 |
18 | gRPC支持多种语言,并能够基于语言自动生成客户端和服务端功能库。目前已提供了C版本grpc、Java版本grpc-java 和 Go版本grpc-go,其它语言的版本正在积极开发中,其中,grpc支持C、C++、Node.js、Python、Ruby、Objective-C、PHP和C\#等语言,grpc-java已经支持Android开发。
19 |
20 | * HTTP2
21 |
22 | gRPC基于HTTP2标准设计,所以相对于其他RPC框架,gRPC带来了更多强大功能,如双向流、头部压缩、多复用请求等。这些功能给移动设备带来重大益处,如节省带宽、降低TCP链接次数、节省CPU使用和延长电池寿命等。同时,gRPC还能够提高了云端服务和Web应用的性能。gRPC既能够在客户端应用,也能够在服务器端应用,从而以透明的方式实现客户端和服务器端的通信和简化通信系统的构建。
23 |
24 | 更多介绍请查看[官方网站](https://grpc.io/)
25 |
--------------------------------------------------------------------------------
/archive/src/proto/google/api/annotations.proto:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2015, Google Inc.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | syntax = "proto3";
16 |
17 | package google.api;
18 |
19 | import "google/api/http.proto";
20 | import "google/protobuf/descriptor.proto";
21 |
22 | option java_multiple_files = true;
23 | option java_outer_classname = "AnnotationsProto";
24 | option java_package = "com.google.api";
25 |
26 | extend google.protobuf.MethodOptions {
27 | // See `HttpRule`.
28 | HttpRule http = 72295728;
29 | }
30 |
--------------------------------------------------------------------------------
/src/interceptor/client_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestPing(t *testing.T) {
8 | tests := []struct {
9 | name string
10 | }{
11 | {name: "ping"},
12 | }
13 | for _, tt := range tests {
14 | t.Run(tt.name, func(t *testing.T) {
15 | Ping()
16 | })
17 | }
18 | }
19 |
20 | func TestMultiPong(t *testing.T) {
21 | tests := []struct {
22 | name string
23 | }{
24 | {name: "multi pong"},
25 | }
26 | for _, tt := range tests {
27 | t.Run(tt.name, func(t *testing.T) {
28 | MultiPong()
29 | })
30 | }
31 | }
32 |
33 | func TestMultiPing(t *testing.T) {
34 | tests := []struct {
35 | name string
36 | }{
37 | {name: "multi ping"},
38 | }
39 | for _, tt := range tests {
40 | t.Run(tt.name, func(t *testing.T) {
41 | MultiPing()
42 | })
43 | }
44 | }
45 |
46 | func TestMultiPingPong(t *testing.T) {
47 | tests := []struct {
48 | name string
49 | }{
50 | {name: "multi ping&pong"},
51 | }
52 | for _, tt := range tests {
53 | t.Run(tt.name, func(t *testing.T) {
54 | MultiPingPong()
55 | })
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/auth/keys/server.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIICyTCCAbGgAwIBAgIJAN3XznX3lP02MA0GCSqGSIb3DQEBCwUAMBYxFDASBgNV
3 | BAMTC2dycGMuc2VydmVyMB4XDTIyMTAwMzA4NDUwOVoXDTMyMDkzMDA4NDUwOVow
4 | FjEUMBIGA1UEAxMLZ3JwYy5zZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
5 | ggEKAoIBAQDNQDxDilpAhIbRze9j3oTwN+CHoypdUFwG6GHeG4vcOgwi4iOfXig7
6 | TVUwThB9qZ91GdNO7/UIIBflbeaZCvOePm/OZOZ1TLJ/XX9hM/W/EEbAKRg8mpB5
7 | ZL35xGzkOfcLHRvCzYvWmKZRN0Vw5zTgR9GvZ5BgCfDS942hOH4J3fIRA2JPRgFe
8 | EaQlrjMOqC2KvR5JIou8nyqMakByPfXAtE0N0Tcj6NSPG/WcMmpXVWtEYLu5RKl3
9 | npanE2qrlzJbgR5vWKoiAWMnucP3b7AVAU5E1a37jWd2pcgsp+upKGVbOsbpHE2C
10 | 8WTqLoArC2UVcim4gMxkS//Hk18NhSupAgMBAAGjGjAYMBYGA1UdEQQPMA2CC2dy
11 | cGMuc2VydmVyMA0GCSqGSIb3DQEBCwUAA4IBAQBtkcf23vHS3jsb3+sVQUYnuS/6
12 | ZGUqPssNooT3tii0fIBOVwvh9c2C6qiqTuuqOzxoEu3mV/vV+yxz8torIPeYin3I
13 | rE5HaKNjtZ8btoPGB3NC3qNz0vcLroqfaYPPMTK5ml0glO0LX0HWE5o1i6WAljTJ
14 | zT8Ap53BohldLIA63+VPaEx/68JggYL5bFTNM7fMmECQePRC91IpgbdtUs3bWgEE
15 | H9S8etJsWaJR1og9RM3K/TkM3/9MUi3jXwvxLgm2YSVTVJQoLOIyJllbXMr1GVH9
16 | 0b711BB8fJl9WIG1Fi08Qu4Rwgz8WGHXwl7cUgSq+BUZFk25wDpJjxHeTAxh
17 | -----END CERTIFICATE-----
18 |
--------------------------------------------------------------------------------
/archive/src/hello/server/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net"
6 |
7 | pb "github.com/jergoo/go-grpc-example/proto/hello" // 引入编译生成的包
8 |
9 | "golang.org/x/net/context"
10 | "google.golang.org/grpc"
11 | "google.golang.org/grpc/grpclog"
12 | )
13 |
14 | const (
15 | // Address gRPC服务地址
16 | Address = "127.0.0.1:50052"
17 | )
18 |
19 | // 定义helloService并实现约定的接口
20 | type helloService struct{}
21 |
22 | // HelloService Hello服务
23 | var HelloService = helloService{}
24 |
25 | // SayHello 实现Hello服务接口
26 | func (h helloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
27 | resp := new(pb.HelloResponse)
28 | resp.Message = fmt.Sprintf("Hello %s.", in.Name)
29 |
30 | return resp, nil
31 | }
32 |
33 | func main() {
34 | listen, err := net.Listen("tcp", Address)
35 | if err != nil {
36 | grpclog.Fatalf("Failed to listen: %v", err)
37 | }
38 |
39 | // 实例化grpc Server
40 | s := grpc.NewServer()
41 |
42 | // 注册HelloService
43 | pb.RegisterHelloServer(s, HelloService)
44 |
45 | grpclog.Println("Listen on " + Address)
46 | s.Serve(listen)
47 | }
48 |
--------------------------------------------------------------------------------
/archive/src/hello_http/server/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "net"
5 |
6 | pb "github.com/jergoo/go-grpc-example/proto/hello_http"
7 | "golang.org/x/net/context"
8 | "google.golang.org/grpc"
9 | "google.golang.org/grpc/grpclog"
10 | )
11 |
12 | const (
13 | // Address gRPC服务地址
14 | Address = "127.0.0.1:50052"
15 | )
16 |
17 | // 定义helloHTTPService并实现约定的接口
18 | type helloHTTPService struct{}
19 |
20 | // HelloHTTPService 实现服务端接口
21 | var HelloHTTPService = helloHTTPService{}
22 |
23 | // SayHello ...
24 | func (h helloHTTPService) SayHello(ctx context.Context, in *pb.HelloHTTPRequest) (*pb.HelloHTTPResponse, error) {
25 | resp := new(pb.HelloHTTPResponse)
26 | resp.Message = "Hello " + in.Name + "."
27 |
28 | return resp, nil
29 | }
30 |
31 | func main() {
32 | listen, err := net.Listen("tcp", Address)
33 | if err != nil {
34 | grpclog.Fatalf("failed to listen: %v", err)
35 | }
36 |
37 | // 实例化grpc Server
38 | s := grpc.NewServer()
39 |
40 | // 注册HelloHTTPService
41 | pb.RegisterHelloHTTPServer(s, HelloHTTPService)
42 |
43 | grpclog.Println("Listen on " + Address)
44 |
45 | s.Serve(listen)
46 | }
47 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Jergoo Mars
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/archive/src/hello_http_2/client/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | pb "github.com/jergoo/go-grpc-example/proto/hello_http" // 引入proto包
5 |
6 | "golang.org/x/net/context"
7 | "google.golang.org/grpc"
8 | "google.golang.org/grpc/credentials" // 引入grpc认证包
9 | "google.golang.org/grpc/grpclog"
10 | )
11 |
12 | const (
13 | // Address gRPC服务地址
14 | Address = "127.0.0.1:50052"
15 | )
16 |
17 | func main() {
18 | // TLS连接
19 | creds, err := credentials.NewClientTLSFromFile("../../keys/server.pem", "server name")
20 | if err != nil {
21 | grpclog.Fatalf("Failed to create TLS credentials %v", err)
22 | }
23 | conn, err := grpc.Dial(Address, grpc.WithTransportCredentials(creds))
24 |
25 | if err != nil {
26 | grpclog.Fatalln(err)
27 | }
28 |
29 | defer conn.Close()
30 |
31 | // 初始化客户端
32 | c := pb.NewHelloHTTPClient(conn)
33 |
34 | // 调用方法
35 | reqBody := new(pb.HelloHTTPRequest)
36 | reqBody.Name = "gRPC"
37 | r, err := c.SayHello(context.Background(), reqBody)
38 |
39 | if err != nil {
40 | grpclog.Fatalln(err)
41 | }
42 |
43 | grpclog.Println(r.Message)
44 | }
45 |
46 | // OR: curl -X POST -k https://localhost:50052/example/echo -d '{"name": "gRPC-HTTP is working!"}'
47 |
--------------------------------------------------------------------------------
/src/protos/example/example.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3"; // 指定proto版本
2 | package example; // 指定包名
3 | option go_package="protos/example"; // 指定go包路径
4 |
5 | // ExampleService 示例
6 | service ExampleService {
7 | // Single 单次请求响应模式
8 | rpc Single(Request) returns (Response);
9 | // ServerStream 服务端流模式
10 | rpc ServerStream(Request) returns (stream Response);
11 | // ClientStream 客户端流模式
12 | rpc ClientStream(stream Request) returns (Response);
13 | // BiStream 双向流模式
14 | rpc BiStream(stream Request) returns (stream Response);
15 | }
16 |
17 | // Request 请求结构
18 | message Request {
19 | optional string value = 1;
20 | }
21 |
22 | // Response 响应结构
23 | message Response {
24 | string valuee = 1;
25 | }
26 |
27 | // Msg message 数据类型示例
28 | message Msg {
29 | int32 i32 = 1;
30 | int64 i64 = 2;
31 | float f32 = 3;
32 | double f64 = 4;
33 | string str = 5;
34 | bool boolean = 6;
35 | bytes byteArr = 7;
36 | map dict = 8;
37 | Status status = 9;
38 | EmbMsg embMsg = 10;
39 | repeated int64 intArr = 11;
40 | }
41 |
42 | message EmbMsg {
43 | string value = 1;
44 | }
45 |
46 | // Status 枚举
47 | enum Status {
48 | OK = 0;
49 | FAIL = 1;
50 | }
51 |
--------------------------------------------------------------------------------
/archive/src/keys/server.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDLTCCArOgAwIBAgIJAK6v8AGZwTeWMAkGByqGSM49BAEwgYUxCzAJBgNVBAYT
3 | AkNOMRAwDgYDVQQIEwdCZWlqaW5nMRAwDgYDVQQHEwdCZWlqaW5nMRIwEAYDVQQK
4 | EwlYWCBDby5MdGQxDDAKBgNVBAsTA2RldjEUMBIGA1UEAxMLc2VydmVyIG5hbWUx
5 | GjAYBgkqhkiG9w0BCQEWC3h4eEB4eHguY29tMB4XDTE2MDcyODA0MzkzNloXDTI2
6 | MDcyNjA0MzkzNlowgYUxCzAJBgNVBAYTAkNOMRAwDgYDVQQIEwdCZWlqaW5nMRAw
7 | DgYDVQQHEwdCZWlqaW5nMRIwEAYDVQQKEwlYWCBDby5MdGQxDDAKBgNVBAsTA2Rl
8 | djEUMBIGA1UEAxMLc2VydmVyIG5hbWUxGjAYBgkqhkiG9w0BCQEWC3h4eEB4eHgu
9 | Y29tMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEPRQ/QvvK5wnz1uIcvykRTnmLl7x+
10 | UwXOLbarylALJqZ0FWLsWKa+ZNqfsNbETdJfAfrjJhV85gdcAkohsDqwhBIdsBWq
11 | O83nktChXO42acui5HIenkugyuzWsq3RDcC8o4HtMIHqMB0GA1UdDgQWBBQ9XXa5
12 | nGN/C43KGh22VsyC3vs9KTCBugYDVR0jBIGyMIGvgBQ9XXa5nGN/C43KGh22VsyC
13 | 3vs9KaGBi6SBiDCBhTELMAkGA1UEBhMCQ04xEDAOBgNVBAgTB0JlaWppbmcxEDAO
14 | BgNVBAcTB0JlaWppbmcxEjAQBgNVBAoTCVhYIENvLkx0ZDEMMAoGA1UECxMDZGV2
15 | MRQwEgYDVQQDEwtzZXJ2ZXIgbmFtZTEaMBgGCSqGSIb3DQEJARYLeHh4QHh4eC5j
16 | b22CCQCur/ABmcE3ljAMBgNVHRMEBTADAQH/MAkGByqGSM49BAEDaQAwZgIxAJBX
17 | 9oK1XpIqp2oWddPyrwoYVOv6SUiTgYxrmBA6Uw9+BauPQqSOE7ZSG53lMfYBewIx
18 | AKSvcxs6tQ5/qPN6Ukn34tC9HafmGgdUN42LB8QTkR+97EueZ7wdWRjP+MIzfOXB
19 | Cg==
20 | -----END CERTIFICATE-----
21 |
--------------------------------------------------------------------------------
/docs/ayu-highlight.css:
--------------------------------------------------------------------------------
1 | /*
2 | Based off of the Ayu theme
3 | Original by Dempfi (https://github.com/dempfi/ayu)
4 | */
5 |
6 | .hljs {
7 | display: block;
8 | overflow-x: auto;
9 | background: #191f26;
10 | color: #e6e1cf;
11 | }
12 |
13 | .hljs-comment,
14 | .hljs-quote {
15 | color: #5c6773;
16 | font-style: italic;
17 | }
18 |
19 | .hljs-variable,
20 | .hljs-template-variable,
21 | .hljs-attribute,
22 | .hljs-attr,
23 | .hljs-regexp,
24 | .hljs-link,
25 | .hljs-selector-id,
26 | .hljs-selector-class {
27 | color: #ff7733;
28 | }
29 |
30 | .hljs-number,
31 | .hljs-meta,
32 | .hljs-builtin-name,
33 | .hljs-literal,
34 | .hljs-type,
35 | .hljs-params {
36 | color: #ffee99;
37 | }
38 |
39 | .hljs-string,
40 | .hljs-bullet {
41 | color: #b8cc52;
42 | }
43 |
44 | .hljs-title,
45 | .hljs-built_in,
46 | .hljs-section {
47 | color: #ffb454;
48 | }
49 |
50 | .hljs-keyword,
51 | .hljs-selector-tag,
52 | .hljs-symbol {
53 | color: #ff7733;
54 | }
55 |
56 | .hljs-name {
57 | color: #36a3d9;
58 | }
59 |
60 | .hljs-tag {
61 | color: #00568d;
62 | }
63 |
64 | .hljs-emphasis {
65 | font-style: italic;
66 | }
67 |
68 | .hljs-strong {
69 | font-weight: bold;
70 | }
71 |
72 | .hljs-addition {
73 | color: #91b362;
74 | }
75 |
76 | .hljs-deletion {
77 | color: #d96c75;
78 | }
79 |
--------------------------------------------------------------------------------
/archive/src/hello_tls/server/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net"
6 |
7 | pb "github.com/jergoo/go-grpc-example/proto/hello"
8 |
9 | "golang.org/x/net/context"
10 | "google.golang.org/grpc"
11 | "google.golang.org/grpc/credentials" // 引入grpc认证包
12 | "google.golang.org/grpc/grpclog"
13 | )
14 |
15 | const (
16 | // Address gRPC服务地址
17 | Address = "127.0.0.1:50052"
18 | )
19 |
20 | // 定义helloService并实现约定的接口
21 | type helloService struct{}
22 |
23 | // HelloService Hello服务
24 | var HelloService = helloService{}
25 |
26 | // SayHello 实现Hello服务接口
27 | func (h helloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
28 | resp := new(pb.HelloResponse)
29 | resp.Message = fmt.Sprintf("Hello %s.", in.Name)
30 |
31 | return resp, nil
32 | }
33 |
34 | func main() {
35 | listen, err := net.Listen("tcp", Address)
36 | if err != nil {
37 | grpclog.Fatalf("Failed to listen: %v", err)
38 | }
39 |
40 | // TLS认证
41 | creds, err := credentials.NewServerTLSFromFile("../../keys/server.pem", "../../keys/server.key")
42 | if err != nil {
43 | grpclog.Fatalf("Failed to generate credentials %v", err)
44 | }
45 |
46 | // 实例化grpc Server, 并开启TLS认证
47 | s := grpc.NewServer(grpc.Creds(creds))
48 |
49 | // 注册HelloService
50 | pb.RegisterHelloServer(s, HelloService)
51 |
52 | grpclog.Println("Listen on " + Address + " with TLS")
53 |
54 | s.Serve(listen)
55 | }
56 |
--------------------------------------------------------------------------------
/archive/src/hello_trace/server/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net"
6 | "net/http"
7 |
8 | pb "github.com/jergoo/go-grpc-example/proto/hello" // 引入编译生成的包
9 |
10 | "golang.org/x/net/context"
11 | "golang.org/x/net/trace"
12 | "google.golang.org/grpc"
13 | "google.golang.org/grpc/grpclog"
14 | )
15 |
16 | const (
17 | // Address gRPC服务地址
18 | Address = "127.0.0.1:50052"
19 | )
20 |
21 | // 定义helloService并实现约定的接口
22 | type helloService struct{}
23 |
24 | // HelloService Hello服务
25 | var HelloService = helloService{}
26 |
27 | // SayHello 实现Hello服务接口
28 | func (h helloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
29 | resp := new(pb.HelloResponse)
30 | resp.Message = fmt.Sprintf("Hello %s.", in.Name)
31 |
32 | return resp, nil
33 | }
34 |
35 | func main() {
36 | listen, err := net.Listen("tcp", Address)
37 | if err != nil {
38 | grpclog.Fatalf("failed to listen: %v", err)
39 | }
40 |
41 | // 实例化grpc Server
42 | s := grpc.NewServer()
43 |
44 | // 注册HelloService
45 | pb.RegisterHelloServer(s, HelloService)
46 |
47 | // 开启trace
48 | go startTrace()
49 |
50 | grpclog.Println("Listen on " + Address)
51 | s.Serve(listen)
52 | }
53 |
54 | func startTrace() {
55 | trace.AuthRequest = func(req *http.Request) (any, sensitive bool) {
56 | return true, true
57 | }
58 | go http.ListenAndServe(":50051", nil)
59 | grpclog.Println("Trace listen on 50051")
60 | }
61 |
--------------------------------------------------------------------------------
/src/auth/client.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "log"
6 |
7 | "google.golang.org/grpc"
8 | "google.golang.org/grpc/credentials"
9 |
10 | pb "github.com/jergoo/go-grpc-tutorial/protos/ping" // 引入编译生成的包
11 | )
12 |
13 | // Ping 单次请求-响应模式
14 | func Ping() {
15 | // 读取服务端证书,并制定对应服务名
16 | creds, err := credentials.NewClientTLSFromFile("keys/server.crt", "grpc.server")
17 | if err != nil {
18 | log.Fatalf("load crt fail: %v", err)
19 | }
20 |
21 | // 连接配置
22 | opts := []grpc.DialOption{
23 | // TLS 认证
24 | grpc.WithTransportCredentials(creds),
25 | // Token 认证
26 | grpc.WithPerRPCCredentials(CustomAuth{Token: "1234567890"}),
27 | }
28 | conn, err := grpc.Dial("localhost:1234", opts...)
29 | if err != nil {
30 | log.Fatal(err)
31 | }
32 | defer conn.Close()
33 |
34 | // 实例化客户端并调用
35 | client := pb.NewPingPongClient(conn)
36 | res, err := client.Ping(context.Background(), &pb.PingRequest{Value: "ping"})
37 | if err != nil {
38 | log.Fatal(err)
39 | }
40 | log.Println(res.Value)
41 | }
42 |
43 | // CustomAuth 自定义认证类型
44 | type CustomAuth struct {
45 | Token string
46 | }
47 |
48 | // GetRequestMetadata 生成认证信息
49 | func (a CustomAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
50 | return map[string]string{
51 | "authorization": a.Token,
52 | }, nil
53 | }
54 |
55 | // RequireTransportSecurity 是否开启 TLS
56 | func (a CustomAuth) RequireTransportSecurity() bool {
57 | return true
58 | }
59 |
--------------------------------------------------------------------------------
/docs/highlight.css:
--------------------------------------------------------------------------------
1 | /*
2 | * An increased contrast highlighting scheme loosely based on the
3 | * "Base16 Atelier Dune Light" theme by Bram de Haan
4 | * (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune)
5 | * Original Base16 color scheme by Chris Kempson
6 | * (https://github.com/chriskempson/base16)
7 | */
8 |
9 | /* Comment */
10 | .hljs-comment,
11 | .hljs-quote {
12 | color: #575757;
13 | }
14 |
15 | /* Red */
16 | .hljs-variable,
17 | .hljs-template-variable,
18 | .hljs-attribute,
19 | .hljs-tag,
20 | .hljs-name,
21 | .hljs-regexp,
22 | .hljs-link,
23 | .hljs-name,
24 | .hljs-selector-id,
25 | .hljs-selector-class {
26 | color: #d70025;
27 | }
28 |
29 | /* Orange */
30 | .hljs-number,
31 | .hljs-meta,
32 | .hljs-built_in,
33 | .hljs-builtin-name,
34 | .hljs-literal,
35 | .hljs-type,
36 | .hljs-params {
37 | color: #b21e00;
38 | }
39 |
40 | /* Green */
41 | .hljs-string,
42 | .hljs-symbol,
43 | .hljs-bullet {
44 | color: #008200;
45 | }
46 |
47 | /* Blue */
48 | .hljs-title,
49 | .hljs-section {
50 | color: #0030f2;
51 | }
52 |
53 | /* Purple */
54 | .hljs-keyword,
55 | .hljs-selector-tag {
56 | color: #9d00ec;
57 | }
58 |
59 | .hljs {
60 | display: block;
61 | overflow-x: auto;
62 | background: #f6f7f6;
63 | color: #000;
64 | }
65 |
66 | .hljs-emphasis {
67 | font-style: italic;
68 | }
69 |
70 | .hljs-strong {
71 | font-weight: bold;
72 | }
73 |
74 | .hljs-addition {
75 | color: #22863a;
76 | background-color: #f0fff4;
77 | }
78 |
79 | .hljs-deletion {
80 | color: #b31d28;
81 | background-color: #ffeef0;
82 | }
83 |
--------------------------------------------------------------------------------
/theme/pagetoc.css:
--------------------------------------------------------------------------------
1 | @media only screen and (max-width:1439px) {
2 | .sidetoc {
3 | display: none;
4 | }
5 | }
6 |
7 | @media only screen and (min-width:1440px) {
8 | main {
9 | position: relative;
10 | }
11 | .sidetoc {
12 | margin-left: auto;
13 | margin-right: auto;
14 | left: calc(100% + (var(--content-max-width))/4 - 140px);
15 | position: absolute;
16 | }
17 | .pagetoc {
18 | position: fixed;
19 | width: 180px;
20 | height: calc(100vh - var(--menu-bar-height) - 0.67em * 4);
21 | overflow: auto;
22 | font-size: 1.2rem;
23 | }
24 | .pagetoc a {
25 | border-left: 1px solid var(--sidebar-bg);
26 | color: var(--fg) !important;
27 | display: block;
28 | padding-bottom: 5px;
29 | padding-top: 5px;
30 | padding-left: 10px;
31 | text-align: left;
32 | text-decoration: none;
33 | }
34 | .pagetoc a:hover,
35 | .pagetoc a.active {
36 | background: var(--sidebar-bg);
37 | color: var(--sidebar-fg) !important;
38 | }
39 | .pagetoc .active {
40 | background: var(--sidebar-bg);
41 | color: var(--sidebar-fg);
42 | }
43 | .pagetoc a[class^='pagetoc-H']:only-child {
44 | display: none !important;
45 | }
46 | .pagetoc .pagetoc-H2 {
47 | padding-left: 20px;
48 | }
49 | .pagetoc .pagetoc-H3 {
50 | padding-left: 40px;
51 | }
52 | .pagetoc .pagetoc-H4 {
53 | padding-left: 60px;
54 | }
55 | .pagetoc .pagetoc-H5 {
56 | display: none;
57 | }
58 | .pagetoc .pagetoc-H6 {
59 | display: none;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/docs/theme/pagetoc.css:
--------------------------------------------------------------------------------
1 | @media only screen and (max-width:1439px) {
2 | .sidetoc {
3 | display: none;
4 | }
5 | }
6 |
7 | @media only screen and (min-width:1440px) {
8 | main {
9 | position: relative;
10 | }
11 | .sidetoc {
12 | margin-left: auto;
13 | margin-right: auto;
14 | left: calc(100% + (var(--content-max-width))/4 - 140px);
15 | position: absolute;
16 | }
17 | .pagetoc {
18 | position: fixed;
19 | width: 180px;
20 | height: calc(100vh - var(--menu-bar-height) - 0.67em * 4);
21 | overflow: auto;
22 | font-size: 1.2rem;
23 | }
24 | .pagetoc a {
25 | border-left: 1px solid var(--sidebar-bg);
26 | color: var(--fg) !important;
27 | display: block;
28 | padding-bottom: 5px;
29 | padding-top: 5px;
30 | padding-left: 10px;
31 | text-align: left;
32 | text-decoration: none;
33 | }
34 | .pagetoc a:hover,
35 | .pagetoc a.active {
36 | background: var(--sidebar-bg);
37 | color: var(--sidebar-fg) !important;
38 | }
39 | .pagetoc .active {
40 | background: var(--sidebar-bg);
41 | color: var(--sidebar-fg);
42 | }
43 | .pagetoc a[class^='pagetoc-H']:only-child {
44 | display: none !important;
45 | }
46 | .pagetoc .pagetoc-H2 {
47 | padding-left: 20px;
48 | }
49 | .pagetoc .pagetoc-H3 {
50 | padding-left: 40px;
51 | }
52 | .pagetoc .pagetoc-H4 {
53 | padding-left: 60px;
54 | }
55 | .pagetoc .pagetoc-H5 {
56 | display: none;
57 | }
58 | .pagetoc .pagetoc-H6 {
59 | display: none;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/auth/keys/server.key:
--------------------------------------------------------------------------------
1 | -----BEGIN PRIVATE KEY-----
2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDNQDxDilpAhIbR
3 | ze9j3oTwN+CHoypdUFwG6GHeG4vcOgwi4iOfXig7TVUwThB9qZ91GdNO7/UIIBfl
4 | beaZCvOePm/OZOZ1TLJ/XX9hM/W/EEbAKRg8mpB5ZL35xGzkOfcLHRvCzYvWmKZR
5 | N0Vw5zTgR9GvZ5BgCfDS942hOH4J3fIRA2JPRgFeEaQlrjMOqC2KvR5JIou8nyqM
6 | akByPfXAtE0N0Tcj6NSPG/WcMmpXVWtEYLu5RKl3npanE2qrlzJbgR5vWKoiAWMn
7 | ucP3b7AVAU5E1a37jWd2pcgsp+upKGVbOsbpHE2C8WTqLoArC2UVcim4gMxkS//H
8 | k18NhSupAgMBAAECggEABIm8C14+uphzhqX/+fxMgicqHrWb1S5WqoxuxYNkZz0d
9 | UZQgZ4CkcRM3o/bF5JFghi1I/8DMcR6MiLUSWSXl1wTYYf7XHZbRh4FgtJx6fXv1
10 | pIAEGirssNJqZOBT7APgE23UVE1JHziGzNrk/ScPgznzR+aJSaZOnct9ZSgjmUI1
11 | 2gO4XuQxCrcswrD95FjivRLOYY3iDd4c3Ls1TaUAfyRl177D9vfFAH/30iT9o0AA
12 | wbrLy8iFty35vv8i6bfNXMonDpTRMJ/3dSXnczpedcGJu2KhEjAq95SUL8EfL/86
13 | dFQhvkpYCYIwiSom3wXQwduiarzI2IkK4i66OvHh0QKBgQDlw1bBL8GreTNTDA4j
14 | xgsrRfkVTGzoxNvTEt/EEzQRgXV9yya0O/G41B2qhR32JpH3m4Q7znr4j5pSpKDA
15 | NnGy7+BDXvPf/P+l3cpYplhuVJ4ClbLW/AT0vgH098ORaaY3YV0WbB9wt7La17Fi
16 | vDZkbIq6Ozz7fxqMHnycRvcElQKBgQDksFV2FVjiuCG41MQsum4s2qpWTnZ05mtn
17 | 7tb6uAc3b1G3Ylwemkad5CngAY8akw62qofHuI5q0c90EdbbEY2LVGSjGwQwiYGi
18 | 8bYSP/xH6brW6px8wBCi0ilBNyJgDqxTJ9/L+Yb2nUTkSjIy9M5T7kR+cNy2VnXt
19 | WU0adq3RxQKBgGrpNRIZhbWjZOVsw5uTyJivK5Lkh/zbtSoHK0YSZXUL1z7ca7sL
20 | 9ZM6aD9zR5jFI8CihapKjt/f+kAgSIiIDOnlQmY25aXBFLf6dvA/HwDfsNKlE52D
21 | kU7Mpx50t3SPQ3+bd3Q6hXquj7PTD8BQscKS2w6DCAJCh3jRqswRHVntAoGAYqEu
22 | apwwVq/FU3roFMZfCBdSyaBTdH2AKzYWH9HIwv7OxoG0azpsZGJoMQVbG8H64YtQ
23 | vjF5stZEW7Hp8VdzrXi7YqEfQHO9pnIK7XoNK1LB0zkiPqdzRVXQlyDZQYZyifaz
24 | jbxTmf8gHDlnxxRP/GDRx5qThkxp1fDi0RwIQWUCgYEAs1uHhxpF+iWy0oOnglFX
25 | ZqZJjbP5BshM9Gj9tSmIO9eJbyaimVs3/Tp2iETmlgqckItYMzOeI8FLcls5NLi0
26 | BWPBum0mNwwvhWK60roNzrul04zlvnLTII89i5p07+zzR4AoySLZbH5+uzghEgmZ
27 | LR6qiogzv/GJ8FC1wqj2DYg=
28 | -----END PRIVATE KEY-----
29 |
--------------------------------------------------------------------------------
/src/metadata/server.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "log"
6 | "net"
7 |
8 | "google.golang.org/grpc"
9 | "google.golang.org/grpc/metadata"
10 |
11 | pb "github.com/jergoo/go-grpc-tutorial/protos/ping" // 引入编译生成的包
12 | )
13 |
14 | // PingPongServer 实现 pb.PingPongServer 接口
15 | type PingPongServer struct {
16 | pb.UnimplementedPingPongServer // 兼容性需要,避免未实现server接口全部方法
17 | }
18 |
19 | // Ping 单次请求-响应模式
20 | func (s *PingPongServer) Ping(ctx context.Context, req *pb.PingRequest) (*pb.PongResponse, error) {
21 | // 读取请求metadata
22 | md, ok := metadata.FromIncomingContext(ctx)
23 | if ok {
24 | log.Printf("Got md: %v", md)
25 | }
26 |
27 | // 设置响应metadata
28 | grpc.SetHeader(ctx, metadata.New(map[string]string{"rkey": "rval"}))
29 |
30 | return &pb.PongResponse{Value: "pong"}, nil
31 | }
32 |
33 | // MultiPong 服务端流模式
34 | func (s *PingPongServer) MultiPong(req *pb.PingRequest, stream pb.PingPong_MultiPongServer) error {
35 | // 读取请求metadata
36 | md, ok := metadata.FromIncomingContext(stream.Context())
37 | if ok {
38 | log.Printf("Got md: %v", md)
39 | }
40 |
41 | // 设置响应metadata
42 | err := stream.SendHeader(metadata.New(map[string]string{"rkey": "rval"}))
43 | if err != nil {
44 | return err
45 | }
46 |
47 | for i := 0; i < 3; i++ {
48 | data := &pb.PongResponse{Value: "pong"}
49 | // 发送消息
50 | err := stream.Send(data)
51 | if err != nil {
52 | return err
53 | }
54 | if err != nil {
55 | log.Println(err)
56 | }
57 | }
58 | return nil
59 | }
60 |
61 | // 启动server
62 | func main() {
63 | srv := grpc.NewServer()
64 | // 注册 PingPongServer
65 | pb.RegisterPingPongServer(srv, &PingPongServer{})
66 | lis, err := net.Listen("tcp", ":1234")
67 | if err != nil {
68 | log.Fatal(err)
69 | }
70 | log.Println("listen on 1234")
71 | srv.Serve(lis)
72 | }
73 |
--------------------------------------------------------------------------------
/archive/src/hello_token/client/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | pb "github.com/jergoo/go-grpc-example/proto/hello" // 引入proto包
5 |
6 | "golang.org/x/net/context"
7 | "google.golang.org/grpc"
8 | "google.golang.org/grpc/credentials" // 引入grpc认证包
9 | "google.golang.org/grpc/grpclog"
10 | )
11 |
12 | const (
13 | // Address gRPC服务地址
14 | Address = "127.0.0.1:50052"
15 |
16 | // OpenTLS 是否开启TLS认证
17 | OpenTLS = true
18 | )
19 |
20 | // customCredential 自定义认证
21 | type customCredential struct{}
22 |
23 | // GetRequestMetadata 实现自定义认证接口
24 | func (c customCredential) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
25 | return map[string]string{
26 | "appid": "101010",
27 | "appkey": "i am key",
28 | }, nil
29 | }
30 |
31 | // RequireTransportSecurity 自定义认证是否开启TLS
32 | func (c customCredential) RequireTransportSecurity() bool {
33 | return OpenTLS
34 | }
35 |
36 | func main() {
37 | var err error
38 | var opts []grpc.DialOption
39 |
40 | if OpenTLS {
41 | // TLS连接
42 | creds, err := credentials.NewClientTLSFromFile("../../keys/server.pem", "server name")
43 | if err != nil {
44 | grpclog.Fatalf("Failed to create TLS credentials %v", err)
45 | }
46 | opts = append(opts, grpc.WithTransportCredentials(creds))
47 | } else {
48 | opts = append(opts, grpc.WithInsecure())
49 | }
50 |
51 | // 使用自定义认证
52 | opts = append(opts, grpc.WithPerRPCCredentials(new(customCredential)))
53 |
54 | conn, err := grpc.Dial(Address, opts...)
55 |
56 | if err != nil {
57 | grpclog.Fatalln(err)
58 | }
59 |
60 | defer conn.Close()
61 |
62 | // 初始化客户端
63 | c := pb.NewHelloClient(conn)
64 |
65 | // 调用方法
66 | req := &pb.HelloRequest{Name: "gRPC"}
67 | res, err := c.SayHello(context.Background(), req)
68 | if err != nil {
69 | grpclog.Fatalln(err)
70 | }
71 |
72 | grpclog.Println(res.Message)
73 | }
74 |
--------------------------------------------------------------------------------
/theme/pagetoc.js:
--------------------------------------------------------------------------------
1 | // Un-active everything when you click it
2 | Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function(el) {
3 | el.addEventHandler("click", function() {
4 | Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function(el) {
5 | el.classList.remove("active");
6 | });
7 | el.classList.add("active");
8 | });
9 | });
10 |
11 | var updateFunction = function() {
12 |
13 | var id;
14 | var elements = document.getElementsByClassName("header");
15 | Array.prototype.forEach.call(elements, function(el) {
16 | if (window.pageYOffset >= el.offsetTop) {
17 | id = el;
18 | }
19 | });
20 |
21 | Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function(el) {
22 | el.classList.remove("active");
23 | });
24 | if (!id) return;
25 | Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function(el) {
26 | if (id.href.localeCompare(el.href) == 0) {
27 | el.classList.add("active");
28 | }
29 | });
30 | };
31 |
32 | // Populate sidebar on load
33 | window.addEventListener('load', function() {
34 | var pagetoc = document.getElementsByClassName("pagetoc")[0];
35 | var elements = document.getElementsByClassName("header");
36 | Array.prototype.forEach.call(elements, function (el) {
37 | var link = document.createElement("a");
38 | link.appendChild(document.createTextNode(el.text));
39 | link.href = el.href;
40 | link.classList.add("pagetoc-" + el.parentElement.tagName);
41 | pagetoc.appendChild(link);
42 | });
43 | updateFunction.call();
44 | });
45 |
46 |
47 |
48 | // Handle active elements on scroll
49 | window.addEventListener("scroll", updateFunction);
50 |
--------------------------------------------------------------------------------
/docs/theme/pagetoc.js:
--------------------------------------------------------------------------------
1 | // Un-active everything when you click it
2 | Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function(el) {
3 | el.addEventHandler("click", function() {
4 | Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function(el) {
5 | el.classList.remove("active");
6 | });
7 | el.classList.add("active");
8 | });
9 | });
10 |
11 | var updateFunction = function() {
12 |
13 | var id;
14 | var elements = document.getElementsByClassName("header");
15 | Array.prototype.forEach.call(elements, function(el) {
16 | if (window.pageYOffset >= el.offsetTop) {
17 | id = el;
18 | }
19 | });
20 |
21 | Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function(el) {
22 | el.classList.remove("active");
23 | });
24 | if (!id) return;
25 | Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function(el) {
26 | if (id.href.localeCompare(el.href) == 0) {
27 | el.classList.add("active");
28 | }
29 | });
30 | };
31 |
32 | // Populate sidebar on load
33 | window.addEventListener('load', function() {
34 | var pagetoc = document.getElementsByClassName("pagetoc")[0];
35 | var elements = document.getElementsByClassName("header");
36 | Array.prototype.forEach.call(elements, function (el) {
37 | var link = document.createElement("a");
38 | link.appendChild(document.createTextNode(el.text));
39 | link.href = el.href;
40 | link.classList.add("pagetoc-" + el.parentElement.tagName);
41 | pagetoc.appendChild(link);
42 | });
43 | updateFunction.call();
44 | });
45 |
46 |
47 |
48 | // Handle active elements on scroll
49 | window.addEventListener("scroll", updateFunction);
50 |
--------------------------------------------------------------------------------
/src/auth/server.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "log"
7 | "net"
8 |
9 | "google.golang.org/grpc"
10 | "google.golang.org/grpc/codes"
11 | "google.golang.org/grpc/credentials"
12 | "google.golang.org/grpc/metadata"
13 |
14 | pb "github.com/jergoo/go-grpc-tutorial/protos/ping" // 引入编译生成的包
15 | )
16 |
17 | // PingPongServer 实现 pb.PingPongServer 接口
18 | type PingPongServer struct {
19 | pb.UnimplementedPingPongServer // 兼容性需要,避免未实现server接口全部方法
20 | }
21 |
22 | // Ping 单次请求-响应模式
23 | func (s *PingPongServer) Ping(ctx context.Context, req *pb.PingRequest) (*pb.PongResponse, error) {
24 | return &pb.PongResponse{Value: "pong"}, nil
25 | }
26 |
27 | // 服务端拦截器 - token 认证
28 | func authInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
29 | md, ok := metadata.FromIncomingContext(ctx)
30 | if !ok {
31 | return nil, errors.New("authorization missing")
32 | }
33 |
34 | var token string
35 | if auth, ok := md["authorization"]; ok {
36 | token = auth[0]
37 | }
38 | if token != "1234567890" {
39 | return nil, grpc.Errorf(codes.Unauthenticated, "token invalid")
40 | }
41 |
42 | // 处理请求
43 | return handler(ctx, req)
44 | }
45 |
46 | // 启动server
47 | func main() {
48 | // 加载证书文件
49 | creds, err := credentials.NewServerTLSFromFile("keys/server.crt", "keys/server.key")
50 | if err != nil {
51 | log.Fatalf("load crt fail:%v", err)
52 | }
53 | opts := []grpc.ServerOption{
54 | grpc.Creds(creds), // TLS
55 | grpc.UnaryInterceptor(authInterceptor), // Token
56 | }
57 |
58 | srv := grpc.NewServer(opts...)
59 | // 注册 PingPongServer
60 | pb.RegisterPingPongServer(srv, &PingPongServer{})
61 | lis, err := net.Listen("tcp", ":1234")
62 | if err != nil {
63 | log.Fatal(err)
64 | }
65 | log.Println("listen on 1234")
66 | srv.Serve(lis)
67 | }
68 |
--------------------------------------------------------------------------------
/docs/favicon.svg:
--------------------------------------------------------------------------------
1 |
22 |
23 |
--------------------------------------------------------------------------------
/src/go.sum:
--------------------------------------------------------------------------------
1 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
2 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
3 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
4 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
5 | github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
6 | golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
7 | golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
8 | golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
9 | golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
10 | golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
11 | golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
12 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
13 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
14 | google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 h1:a2S6M0+660BgMNl++4JPlcAO/CjkqYItDEZwkoDQK7c=
15 | google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
16 | google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
17 | google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
18 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
19 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
20 | google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
21 | google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
22 |
--------------------------------------------------------------------------------
/archive/src/hello_token/server/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net"
6 |
7 | pb "github.com/jergoo/go-grpc-example/proto/hello"
8 |
9 | "golang.org/x/net/context"
10 | "google.golang.org/grpc"
11 | "google.golang.org/grpc/codes"
12 | "google.golang.org/grpc/credentials" // 引入grpc认证包
13 | "google.golang.org/grpc/grpclog"
14 | "google.golang.org/grpc/metadata" // 引入grpc meta包
15 | )
16 |
17 | const (
18 | // Address gRPC服务地址
19 | Address = "127.0.0.1:50052"
20 | )
21 |
22 | // 定义helloService并实现约定的接口
23 | type helloService struct{}
24 |
25 | // HelloService ...
26 | var HelloService = helloService{}
27 |
28 | // SayHello 实现Hello服务接口
29 | func (h helloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
30 | // 解析metada中的信息并验证
31 | md, ok := metadata.FromIncomingContext(ctx)
32 | if !ok {
33 | return nil, grpc.Errorf(codes.Unauthenticated, "无Token认证信息")
34 | }
35 |
36 | var (
37 | appid string
38 | appkey string
39 | )
40 |
41 | if val, ok := md["appid"]; ok {
42 | appid = val[0]
43 | }
44 |
45 | if val, ok := md["appkey"]; ok {
46 | appkey = val[0]
47 | }
48 |
49 | if appid != "101010" || appkey != "i am key" {
50 | return nil, grpc.Errorf(codes.Unauthenticated, "Token认证信息无效: appid=%s, appkey=%s", appid, appkey)
51 | }
52 |
53 | resp := new(pb.HelloResponse)
54 | resp.Message = fmt.Sprintf("Hello %s.\nToken info: appid=%s,appkey=%s", in.Name, appid, appkey)
55 |
56 | return resp, nil
57 | }
58 |
59 | func main() {
60 | listen, err := net.Listen("tcp", Address)
61 | if err != nil {
62 | grpclog.Fatalf("failed to listen: %v", err)
63 | }
64 |
65 | // TLS认证
66 | creds, err := credentials.NewServerTLSFromFile("../../keys/server.pem", "../../keys/server.key")
67 | if err != nil {
68 | grpclog.Fatalf("Failed to generate credentials %v", err)
69 | }
70 |
71 | // 实例化grpc Server, 并开启TLS认证
72 | s := grpc.NewServer(grpc.Creds(creds))
73 |
74 | // 注册HelloService
75 | pb.RegisterHelloServer(s, HelloService)
76 |
77 | grpclog.Println("Listen on " + Address + " with TLS + Token")
78 |
79 | s.Serve(listen)
80 | }
81 |
--------------------------------------------------------------------------------
/src/metadata/client.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "io"
6 | "log"
7 | "strconv"
8 | "time"
9 |
10 | "google.golang.org/grpc"
11 | "google.golang.org/grpc/metadata"
12 |
13 | pb "github.com/jergoo/go-grpc-tutorial/protos/ping" // 引入编译生成的包
14 | )
15 |
16 | // Ping 单次请求-响应模式
17 | func Ping() {
18 | conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure())
19 | if err != nil {
20 | log.Fatal(err)
21 | }
22 | defer conn.Close()
23 |
24 | // 初始化metadata
25 | md := metadata.New(map[string]string{"ts": strconv.FormatInt(time.Now().UnixMicro(), 10)})
26 | md.Set("key", "v1", "v2")
27 |
28 | // md := metadata.Pairs("key", "v1", "key", "v2")
29 |
30 | // md := make(metadata.MD)
31 | // md.Set("key", "v1", "v2")
32 |
33 | // context包装metadata
34 | ctx := metadata.NewOutgoingContext(context.Background(), md)
35 | ctx = metadata.AppendToOutgoingContext(ctx, "k3", "v3")
36 |
37 | // 实例化客户端并调用
38 | client := pb.NewPingPongClient(conn)
39 |
40 | var reponseMD metadata.MD
41 | res, err := client.Ping(ctx, &pb.PingRequest{Value: "ping"}, grpc.Header(&reponseMD))
42 | if err != nil {
43 | log.Fatal(err)
44 | }
45 |
46 | log.Printf("Got response md: %v", reponseMD)
47 | log.Println(res.Value)
48 | }
49 |
50 | // MultiPong 服务端流模式
51 | func MultiPong() {
52 | conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure())
53 | if err != nil {
54 | log.Fatal(err)
55 | }
56 | defer conn.Close()
57 |
58 | // 初始化metadata
59 | md := metadata.New(map[string]string{"ts": strconv.FormatInt(time.Now().UnixMicro(), 10)})
60 | ctx := metadata.NewOutgoingContext(context.Background(), md)
61 |
62 | // 实例化客户端并调用
63 | client := pb.NewPingPongClient(conn)
64 | // 获得对 stream 对象的引用
65 | stream, err := client.MultiPong(ctx, &pb.PingRequest{Value: "ping"})
66 | if err != nil {
67 | log.Fatal(err)
68 | }
69 |
70 | // 读取响应metadata
71 | mdResponse, err := stream.Header()
72 | if err != nil {
73 | log.Fatal(err)
74 | }
75 | log.Printf("get response md: %v", mdResponse)
76 |
77 | // 循环接收数据流
78 | for {
79 | msg, err := stream.Recv()
80 | if err != nil {
81 | if err == io.EOF {
82 | break
83 | }
84 | log.Fatal(err)
85 | }
86 | log.Println(msg.Value)
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/docs/tomorrow-night.css:
--------------------------------------------------------------------------------
1 | /* Tomorrow Night Theme */
2 | /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
3 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */
4 | /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
5 |
6 | /* Tomorrow Comment */
7 | .hljs-comment {
8 | color: #969896;
9 | }
10 |
11 | /* Tomorrow Red */
12 | .hljs-variable,
13 | .hljs-attribute,
14 | .hljs-tag,
15 | .hljs-regexp,
16 | .ruby .hljs-constant,
17 | .xml .hljs-tag .hljs-title,
18 | .xml .hljs-pi,
19 | .xml .hljs-doctype,
20 | .html .hljs-doctype,
21 | .css .hljs-id,
22 | .css .hljs-class,
23 | .css .hljs-pseudo {
24 | color: #cc6666;
25 | }
26 |
27 | /* Tomorrow Orange */
28 | .hljs-number,
29 | .hljs-preprocessor,
30 | .hljs-pragma,
31 | .hljs-built_in,
32 | .hljs-literal,
33 | .hljs-params,
34 | .hljs-constant {
35 | color: #de935f;
36 | }
37 |
38 | /* Tomorrow Yellow */
39 | .ruby .hljs-class .hljs-title,
40 | .css .hljs-rule .hljs-attribute {
41 | color: #f0c674;
42 | }
43 |
44 | /* Tomorrow Green */
45 | .hljs-string,
46 | .hljs-value,
47 | .hljs-inheritance,
48 | .hljs-header,
49 | .hljs-name,
50 | .ruby .hljs-symbol,
51 | .xml .hljs-cdata {
52 | color: #b5bd68;
53 | }
54 |
55 | /* Tomorrow Aqua */
56 | .hljs-title,
57 | .css .hljs-hexcolor {
58 | color: #8abeb7;
59 | }
60 |
61 | /* Tomorrow Blue */
62 | .hljs-function,
63 | .python .hljs-decorator,
64 | .python .hljs-title,
65 | .ruby .hljs-function .hljs-title,
66 | .ruby .hljs-title .hljs-keyword,
67 | .perl .hljs-sub,
68 | .javascript .hljs-title,
69 | .coffeescript .hljs-title {
70 | color: #81a2be;
71 | }
72 |
73 | /* Tomorrow Purple */
74 | .hljs-keyword,
75 | .javascript .hljs-function {
76 | color: #b294bb;
77 | }
78 |
79 | .hljs {
80 | display: block;
81 | overflow-x: auto;
82 | background: #1d1f21;
83 | color: #c5c8c6;
84 | }
85 |
86 | .coffeescript .javascript,
87 | .javascript .xml,
88 | .tex .hljs-formula,
89 | .xml .javascript,
90 | .xml .vbscript,
91 | .xml .css,
92 | .xml .hljs-cdata {
93 | opacity: 0.5;
94 | }
95 |
96 | .hljs-addition {
97 | color: #718c00;
98 | }
99 |
100 | .hljs-deletion {
101 | color: #c82829;
102 | }
103 |
--------------------------------------------------------------------------------
/src/ping/server.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "io"
7 | "log"
8 | "net"
9 |
10 | "google.golang.org/grpc"
11 |
12 | pb "github.com/jergoo/go-grpc-tutorial/protos/ping" // 引入编译生成的包
13 | )
14 |
15 | // PingPongServer 实现 pb.PingPongServer 接口
16 | type PingPongServer struct {
17 | pb.UnimplementedPingPongServer // 兼容性需要,避免未实现server接口全部方法
18 | }
19 |
20 | // Ping 单次请求-响应模式
21 | func (s *PingPongServer) Ping(ctx context.Context, req *pb.PingRequest) (*pb.PongResponse, error) {
22 | return &pb.PongResponse{Value: "pong"}, nil
23 | }
24 |
25 | // MultiPong 服务端流模式
26 | func (s *PingPongServer) MultiPong(req *pb.PingRequest, stream pb.PingPong_MultiPongServer) error {
27 | for i := 0; i < 10; i++ {
28 | data := &pb.PongResponse{Value: "pong"}
29 | // 发送消息
30 | err := stream.Send(data)
31 | if err != nil {
32 | return err
33 | }
34 | }
35 | return nil
36 | }
37 |
38 | // MultiPing 客户端流模式
39 | func (s *PingPongServer) MultiPing(stream pb.PingPong_MultiPingServer) error {
40 | msgs := []string{}
41 | for {
42 | // 提前结束接收消息
43 | if len(msgs) > 5 {
44 | return stream.SendAndClose(&pb.PongResponse{Value: "ping enough, max 5"})
45 | }
46 |
47 | msg, err := stream.Recv()
48 | if err != nil {
49 | // 客户端消息结束,返回响应信息
50 | if err == io.EOF {
51 | return stream.SendAndClose(&pb.PongResponse{Value: fmt.Sprintf("got %d ping", len(msgs))})
52 | }
53 | return err
54 | }
55 | msgs = append(msgs, msg.Value)
56 | }
57 | }
58 |
59 | // MultiPingPong 双向流模式
60 | func (s *PingPongServer) MultiPingPong(stream pb.PingPong_MultiPingPongServer) error {
61 | msgs := []string{}
62 | for {
63 | // 接收消息
64 | msg, err := stream.Recv()
65 | if err != nil {
66 | if err == io.EOF {
67 | break
68 | }
69 | return err
70 | }
71 | msgs = append(msgs, msg.Value)
72 |
73 | // 每收到两个消息响应一次
74 | if len(msgs)%2 == 0 {
75 | err = stream.Send(&pb.PongResponse{Value: "pong"})
76 | if err != nil {
77 | return err
78 | }
79 | }
80 | }
81 | return nil
82 | }
83 |
84 | // 启动server
85 | func main() {
86 | srv := grpc.NewServer()
87 | // 注册 PingPongServer
88 | pb.RegisterPingPongServer(srv, &PingPongServer{})
89 | lis, err := net.Listen("tcp", ":1234")
90 | if err != nil {
91 | log.Fatal(err)
92 | }
93 | log.Println("listen on 1234")
94 | srv.Serve(lis)
95 | }
96 |
--------------------------------------------------------------------------------
/archive/docs/c1/install.md:
--------------------------------------------------------------------------------
1 | # 安装
2 |
3 | 环境:
4 | -------
5 |
6 | * 操作系统:macOS 10.12.5 / Ubuntu 16.04
7 | * golang 版本:1.8.3
8 | * protobuf 版本:3.3.2
9 | * grpc-go 版本:1.4.2
10 |
11 |
12 | 准备工作
13 | -------
14 |
15 | * Linux 安装 [linuxbrew](https://github.com/Homebrew/linuxbrew)
16 | * macOS 安装 [homebrew](http://brew.sh/)
17 |
18 |
19 | protobuf
20 | --------
21 |
22 | 项目地址:[google/protobuf](https://github.com/google/protobuf)
23 |
24 | 这里直接使用`brew`工具安装
25 |
26 | ```sh
27 | $ brew install protobuf
28 | ```
29 | `brew`默认会安装最新版本,执行`protoc`命令查看当前版本:
30 |
31 | ```sh
32 | $ protoc --version
33 | libprotoc 3.3.2
34 | ```
35 |
36 |
37 | grpc-go
38 | -------
39 |
40 | 项目地址:[grpc-go](https://github.com/grpc/grpc-go)
41 |
42 | * 要求golang版本 >= 1.6
43 |
44 | ```sh
45 | $ go get -u google.golang.org/grpc
46 | ```
47 |
48 |
49 | golang protobuf
50 | ---------------
51 |
52 | 项目地址:[golang/protobuf](https://github.com/golang/protobuf)
53 |
54 | * 要求golang版本 > 1.4
55 | * 使用前要求安装protobuf编译器
56 |
57 | ```sh
58 | $ go get -u github.com/golang/protobuf/{proto,protoc-gen-go}
59 | ```
60 |
61 | 安装`protoc-gen-go`到 `$GOPATH/bin`。 注意:该目录必须在系统的环境变量`$PATH`中。
62 |
63 | 如果一路没有问题的话,到此为止,需要的环境都安装好了😀。
64 |
65 |
66 | 编译器使用
67 | --------
68 |
69 | 使用`protoc`命令编译`.proto`文件,不同语言支持需要指定输出参数,如:
70 |
71 | ```sh
72 | $ protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR --go_out=DST_DIR --ruby_out=DST_DIR --javanano_out=DST_DIR --objc_out=DST_DIR --csharp_out=DST_DIR path/to/file.proto
73 | ```
74 |
75 | 这里详细介绍golang的编译姿势:
76 |
77 | * `-I` 参数:指定import路径,可以指定多个`-I`参数,编译时按顺序查找,不指定时默认查找当前目录
78 | * `--go_out` :golang编译支持,支持以下参数
79 |
80 | * `plugins=plugin1+plugin2` - 指定插件,目前只支持grpc,即:`plugins=grpc`
81 | * `M` 参数 - 指定导入的.proto文件路径编译后对应的golang包名(不指定本参数默认就是`.proto`文件中`import`语句的路径)
82 | * `import_prefix=xxx` - 为所有`import`路径添加前缀,主要用于编译子目录内的多个proto文件,这个参数按理说很有用,尤其适用替代一些情况时的`M`参数,但是实际使用时有个蛋疼的问题导致并不能达到我们预想的效果,自己尝试看看吧
83 | * `import_path=foo/bar` - 用于指定未声明`package`或`go_package`的文件的包名,最右面的斜线前的字符会被忽略
84 | * 末尾 `:编译文件路径 .proto文件路径(支持通配符)`
85 |
86 | 完整示例:
87 |
88 | ```sh
89 | $ protoc -I . --go_out=plugins=grpc,Mfoo/bar.proto=bar,import_prefix=foo/,import_path=foo/bar:. ./*.proto
90 | ```
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/archive/src/hello_interceptor/client/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "time"
5 |
6 | pb "github.com/jergoo/go-grpc-example/proto/hello" // 引入proto包
7 |
8 | "golang.org/x/net/context"
9 | "google.golang.org/grpc"
10 | "google.golang.org/grpc/credentials" // 引入grpc认证包
11 | "google.golang.org/grpc/grpclog"
12 | )
13 |
14 | const (
15 | // Address gRPC服务地址
16 | Address = "127.0.0.1:50052"
17 |
18 | // OpenTLS 是否开启TLS认证
19 | OpenTLS = true
20 | )
21 |
22 | // customCredential 自定义认证
23 | type customCredential struct{}
24 |
25 | // GetRequestMetadata 实现自定义认证接口
26 | func (c customCredential) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
27 | return map[string]string{
28 | "appid": "101010",
29 | "appkey": "i am key",
30 | }, nil
31 | }
32 |
33 | // RequireTransportSecurity 自定义认证是否开启TLS
34 | func (c customCredential) RequireTransportSecurity() bool {
35 | return OpenTLS
36 | }
37 |
38 | func main() {
39 | var err error
40 | var opts []grpc.DialOption
41 |
42 | if OpenTLS {
43 | // TLS连接
44 | creds, err := credentials.NewClientTLSFromFile("../../keys/server.pem", "server name")
45 | if err != nil {
46 | grpclog.Fatalf("Failed to create TLS credentials %v", err)
47 | }
48 | opts = append(opts, grpc.WithTransportCredentials(creds))
49 | } else {
50 | opts = append(opts, grpc.WithInsecure())
51 | }
52 |
53 | // 指定自定义认证
54 | opts = append(opts, grpc.WithPerRPCCredentials(new(customCredential)))
55 | // 指定客户端interceptor
56 | opts = append(opts, grpc.WithUnaryInterceptor(interceptor))
57 |
58 | conn, err := grpc.Dial(Address, opts...)
59 | if err != nil {
60 | grpclog.Fatalln(err)
61 | }
62 | defer conn.Close()
63 |
64 | // 初始化客户端
65 | c := pb.NewHelloClient(conn)
66 |
67 | // 调用方法
68 | req := &pb.HelloRequest{Name: "gRPC"}
69 | res, err := c.SayHello(context.Background(), req)
70 | if err != nil {
71 | grpclog.Fatalln(err)
72 | }
73 |
74 | grpclog.Println(res.Message)
75 | }
76 |
77 | // interceptor 客户端拦截器
78 | func interceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
79 | start := time.Now()
80 | err := invoker(ctx, method, req, reply, cc, opts...)
81 | grpclog.Printf("method=%s req=%v rep=%v duration=%s error=%v\n", method, req, reply, time.Since(start), err)
82 | return err
83 | }
84 |
--------------------------------------------------------------------------------
/src/protos/google/protobuf/source_context.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | syntax = "proto3";
32 |
33 | package google.protobuf;
34 |
35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
36 | option java_package = "com.google.protobuf";
37 | option java_outer_classname = "SourceContextProto";
38 | option java_multiple_files = true;
39 | option objc_class_prefix = "GPB";
40 | option go_package = "google.golang.org/protobuf/types/known/sourcecontextpb";
41 |
42 | // `SourceContext` represents information about the source of a
43 | // protobuf element, like the file in which it is defined.
44 | message SourceContext {
45 | // The path-qualified name of the .proto file that contained the associated
46 | // protobuf element. For example: `"google/protobuf/source_context.proto"`.
47 | string file_name = 1;
48 | }
49 |
--------------------------------------------------------------------------------
/src/protos/google/protobuf/empty.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | syntax = "proto3";
32 |
33 | package google.protobuf;
34 |
35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
36 | option go_package = "google.golang.org/protobuf/types/known/emptypb";
37 | option java_package = "com.google.protobuf";
38 | option java_outer_classname = "EmptyProto";
39 | option java_multiple_files = true;
40 | option objc_class_prefix = "GPB";
41 | option cc_enable_arenas = true;
42 |
43 | // A generic empty message that you can re-use to avoid defining duplicated
44 | // empty messages in your APIs. A typical example is to use it as the request
45 | // or the response type of an API method. For instance:
46 | //
47 | // service Foo {
48 | // rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);
49 | // }
50 | //
51 | message Empty {}
52 |
--------------------------------------------------------------------------------
/archive/docs/c2/trace.md:
--------------------------------------------------------------------------------
1 | # 内置Trace
2 |
3 | grpc内置了客户端和服务端的请求追踪,基于`golang.org/x/net/trace`包实现,默认是开启状态,可以查看事件和请求日志,对于基本的请求状态查看调试也是很有帮助的,客户端与服务端基本一致,这里以服务端开启trace server为例,修改hello项目服务端代码:
4 |
5 | ## 目录结构
6 |
7 | ```
8 | |—— hello_trace/
9 | |—— client/
10 | |—— main.go // 客户端
11 | |—— server/
12 | |—— main.go // 服务端
13 | |—— proto/
14 | |—— hello/
15 | |—— hello.proto // proto描述文件
16 | |—— hello.pb.go // proto编译后文件
17 | ```
18 |
19 | ## 示例代码
20 |
21 | ```go
22 | package main
23 |
24 | import (
25 | "fmt"
26 | "net"
27 | "net/http"
28 |
29 | pb "github.com/jergoo/go-grpc-example/proto/hello" // 引入编译生成的包
30 |
31 | "golang.org/x/net/context"
32 | "golang.org/x/net/trace"
33 | "google.golang.org/grpc"
34 | "google.golang.org/grpc/grpclog"
35 | )
36 |
37 | const (
38 | // Address gRPC服务地址
39 | Address = "127.0.0.1:50052"
40 | )
41 |
42 | // 定义helloService并实现约定的接口
43 | type helloService struct{}
44 |
45 | // HelloService Hello服务
46 | var HelloService = helloService{}
47 |
48 | // SayHello 实现Hello服务接口
49 | func (h helloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
50 | resp := new(pb.HelloResponse)
51 | resp.Message = fmt.Sprintf("Hello %s.", in.Name)
52 |
53 | return resp, nil
54 | }
55 |
56 | func main() {
57 | listen, err := net.Listen("tcp", Address)
58 | if err != nil {
59 | grpclog.Fatalf("failed to listen: %v", err)
60 | }
61 |
62 | // 实例化grpc Server
63 | s := grpc.NewServer()
64 |
65 | // 注册HelloService
66 | pb.RegisterHelloServer(s, HelloService)
67 |
68 | // 开启trace
69 | go startTrace()
70 |
71 | grpclog.Println("Listen on " + Address)
72 | s.Serve(listen)
73 | }
74 |
75 | func startTrace() {
76 | trace.AuthRequest = func(req *http.Request) (any, sensitive bool) {
77 | return true, true
78 | }
79 | go http.ListenAndServe(":50051", nil)
80 | grpclog.Println("Trace listen on 50051")
81 | }
82 | ```
83 | 这里我们开启一个http服务监听50051端口,用来查看grpc请求的trace信息
84 |
85 | 运行:
86 |
87 | ```sh
88 | $ go run main.go
89 |
90 | Listen on 127.0.0.1:50052
91 | Trace listen on 50051
92 |
93 | # 进入client目录执行一次客户端请求
94 | ```
95 |
96 |
97 | ## 服务端事件查看
98 |
99 | 访问:localhost:50051/debug/events,结果如图:
100 |
101 | 
102 |
103 | 可以看到服务端注册的服务和服务正常启动的事件信息。
104 |
105 |
106 | ## 请求日志信息查看
107 |
108 | 访问:localhost:50051/debug/requests,结果如图:
109 |
110 | 
111 |
112 | 这里可以显示最近的请求状态,包括请求的服务、参数、耗时、响应,对于简单的状态查看还是很方便的,默认值显示最近10条记录。
113 |
--------------------------------------------------------------------------------
/src/docs/advance/monitor/tracing.md:
--------------------------------------------------------------------------------
1 | # tracing
2 |
3 | ---
4 |
5 | grpc内置了客户端和服务端的请求追踪,基于`golang.org/x/net/trace`包实现,默认是开启状态,可以查看事件和请求日志,对于基本的请求状态查看调试也是很有帮助的,客户端与服务端基本一致,这里以服务端开启trace server为例,修改hello项目服务端代码:
6 |
7 | ## 目录结构
8 |
9 | ```
10 | |—— hello_trace/
11 | |—— client/
12 | |—— main.go // 客户端
13 | |—— server/
14 | |—— main.go // 服务端
15 | |—— proto/
16 | |—— hello/
17 | |—— hello.proto // proto描述文件
18 | |—— hello.pb.go // proto编译后文件
19 | ```
20 |
21 | ## 示例代码
22 |
23 | ```go
24 | package main
25 |
26 | import (
27 | "fmt"
28 | "net"
29 | "net/http"
30 |
31 | pb "github.com/jergoo/go-grpc-tutorial/proto/hello" // 引入编译生成的包
32 |
33 | "golang.org/x/net/context"
34 | "golang.org/x/net/trace"
35 | "google.golang.org/grpc"
36 | "google.golang.org/grpc/grpclog"
37 | )
38 |
39 | const (
40 | // Address gRPC服务地址
41 | Address = "127.0.0.1:50052"
42 | )
43 |
44 | // 定义helloService并实现约定的接口
45 | type helloService struct{}
46 |
47 | // HelloService Hello服务
48 | var HelloService = helloService{}
49 |
50 | // SayHello 实现Hello服务接口
51 | func (h helloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
52 | resp := new(pb.HelloResponse)
53 | resp.Message = fmt.Sprintf("Hello %s.", in.Name)
54 |
55 | return resp, nil
56 | }
57 |
58 | func main() {
59 | listen, err := net.Listen("tcp", Address)
60 | if err != nil {
61 | grpclog.Fatalf("failed to listen: %v", err)
62 | }
63 |
64 | // 实例化grpc Server
65 | s := grpc.NewServer()
66 |
67 | // 注册HelloService
68 | pb.RegisterHelloServer(s, HelloService)
69 |
70 | // 开启trace
71 | go startTrace()
72 |
73 | grpclog.Println("Listen on " + Address)
74 | s.Serve(listen)
75 | }
76 |
77 | func startTrace() {
78 | trace.AuthRequest = func(req *http.Request) (any, sensitive bool) {
79 | return true, true
80 | }
81 | go http.ListenAndServe(":50051", nil)
82 | grpclog.Println("Trace listen on 50051")
83 | }
84 | ```
85 | 这里我们开启一个http服务监听50051端口,用来查看grpc请求的trace信息
86 |
87 | 运行:
88 |
89 | ```sh
90 | $ go run main.go
91 |
92 | Listen on 127.0.0.1:50052
93 | Trace listen on 50051
94 |
95 | # 进入client目录执行一次客户端请求
96 | ```
97 |
98 |
99 | ## 服务端事件查看
100 |
101 | 访问:localhost:50051/debug/events,结果如图:
102 |
103 | 
104 |
105 | 可以看到服务端注册的服务和服务正常启动的事件信息。
106 |
107 |
108 | ## 请求日志信息查看
109 |
110 | 访问:localhost:50051/debug/requests,结果如图:
111 |
112 | 
113 |
114 | 这里可以显示最近的请求状态,包括请求的服务、参数、耗时、响应,对于简单的状态查看还是很方便的,默认值显示最近10条记录。
115 |
--------------------------------------------------------------------------------
/archive/src/hello_interceptor/server/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net"
6 |
7 | pb "github.com/jergoo/go-grpc-example/proto/hello"
8 |
9 | "golang.org/x/net/context"
10 | "google.golang.org/grpc"
11 | "google.golang.org/grpc/codes" // grpc 响应状态码
12 | "google.golang.org/grpc/credentials" // grpc认证包
13 | "google.golang.org/grpc/grpclog"
14 | "google.golang.org/grpc/metadata" // grpc metadata包
15 | )
16 |
17 | const (
18 | // Address gRPC服务地址
19 | Address = "127.0.0.1:50052"
20 | )
21 |
22 | // 定义helloService并实现约定的接口
23 | type helloService struct{}
24 |
25 | // HelloService Hello服务
26 | var HelloService = helloService{}
27 |
28 | // SayHello 实现Hello服务接口
29 | func (h helloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
30 | resp := new(pb.HelloResponse)
31 | resp.Message = fmt.Sprintf("Hello %s.", in.Name)
32 |
33 | return resp, nil
34 | }
35 |
36 | func main() {
37 | listen, err := net.Listen("tcp", Address)
38 | if err != nil {
39 | grpclog.Fatalf("Failed to listen: %v", err)
40 | }
41 |
42 | var opts []grpc.ServerOption
43 |
44 | // TLS认证
45 | creds, err := credentials.NewServerTLSFromFile("../../keys/server.pem", "../../keys/server.key")
46 | if err != nil {
47 | grpclog.Fatalf("Failed to generate credentials %v", err)
48 | }
49 |
50 | opts = append(opts, grpc.Creds(creds))
51 |
52 | // 注册interceptor
53 | opts = append(opts, grpc.UnaryInterceptor(interceptor))
54 |
55 | // 实例化grpc Server
56 | s := grpc.NewServer(opts...)
57 |
58 | // 注册HelloService
59 | pb.RegisterHelloServer(s, HelloService)
60 |
61 | grpclog.Println("Listen on " + Address + " with TLS + Token + Interceptor")
62 |
63 | s.Serve(listen)
64 | }
65 |
66 | // auth 验证Token
67 | func auth(ctx context.Context) error {
68 | md, ok := metadata.FromIncomingContext(ctx)
69 | if !ok {
70 | return grpc.Errorf(codes.Unauthenticated, "无Token认证信息")
71 | }
72 |
73 | var (
74 | appid string
75 | appkey string
76 | )
77 |
78 | if val, ok := md["appid"]; ok {
79 | appid = val[0]
80 | }
81 |
82 | if val, ok := md["appkey"]; ok {
83 | appkey = val[0]
84 | }
85 |
86 | if appid != "101010" || appkey != "i am key" {
87 | return grpc.Errorf(codes.Unauthenticated, "Token认证信息无效: appid=%s, appkey=%s", appid, appkey)
88 | }
89 |
90 | return nil
91 | }
92 |
93 | // interceptor 拦截器
94 | func interceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
95 | err := auth(ctx)
96 | if err != nil {
97 | return nil, err
98 | }
99 | // 继续处理请求
100 | return handler(ctx, req)
101 | }
102 |
--------------------------------------------------------------------------------
/src/protos/google/api/annotations.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go. DO NOT EDIT.
2 | // source: google/api/annotations.proto
3 |
4 | /*
5 | Package google_api is a generated protocol buffer package.
6 |
7 | It is generated from these files:
8 | google/api/annotations.proto
9 | google/api/http.proto
10 |
11 | It has these top-level messages:
12 | Http
13 | HttpRule
14 | CustomHttpPattern
15 | */
16 | package google_api
17 |
18 | import proto "github.com/golang/protobuf/proto"
19 | import fmt "fmt"
20 | import math "math"
21 | import google_protobuf "github.com/golang/protobuf/protoc-gen-go/descriptor"
22 |
23 | // Reference imports to suppress errors if they are not otherwise used.
24 | var _ = proto.Marshal
25 | var _ = fmt.Errorf
26 | var _ = math.Inf
27 |
28 | // This is a compile-time assertion to ensure that this generated file
29 | // is compatible with the proto package it is being compiled against.
30 | // A compilation error at this line likely means your copy of the
31 | // proto package needs to be updated.
32 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
33 |
34 | var E_Http = &proto.ExtensionDesc{
35 | ExtendedType: (*google_protobuf.MethodOptions)(nil),
36 | ExtensionType: (*HttpRule)(nil),
37 | Field: 72295728,
38 | Name: "google.api.http",
39 | Tag: "bytes,72295728,opt,name=http",
40 | Filename: "google/api/annotations.proto",
41 | }
42 |
43 | func init() {
44 | proto.RegisterExtension(E_Http)
45 | }
46 |
47 | func init() { proto.RegisterFile("google/api/annotations.proto", fileDescriptor0) }
48 |
49 | var fileDescriptor0 = []byte{
50 | // 169 bytes of a gzipped FileDescriptorProto
51 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x49, 0xcf, 0xcf, 0x4f,
52 | 0xcf, 0x49, 0xd5, 0x4f, 0x2c, 0xc8, 0xd4, 0x4f, 0xcc, 0xcb, 0xcb, 0x2f, 0x49, 0x2c, 0xc9, 0xcc,
53 | 0xcf, 0x2b, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x82, 0xc8, 0xea, 0x25, 0x16, 0x64,
54 | 0x4a, 0x89, 0x22, 0xa9, 0xcc, 0x28, 0x29, 0x29, 0x80, 0x28, 0x91, 0x52, 0x80, 0x0a, 0x83, 0x79,
55 | 0x49, 0xa5, 0x69, 0xfa, 0x29, 0xa9, 0xc5, 0xc9, 0x45, 0x99, 0x05, 0x25, 0xf9, 0x45, 0x10, 0x15,
56 | 0x56, 0xde, 0x5c, 0x2c, 0x20, 0xf5, 0x42, 0x72, 0x7a, 0x50, 0xd3, 0x60, 0x4a, 0xf5, 0x7c, 0x53,
57 | 0x4b, 0x32, 0xf2, 0x53, 0xfc, 0x0b, 0xc0, 0x56, 0x4a, 0x6c, 0x38, 0xb5, 0x47, 0x49, 0x81, 0x51,
58 | 0x83, 0xdb, 0x48, 0x44, 0x0f, 0x61, 0xad, 0x9e, 0x47, 0x49, 0x49, 0x41, 0x50, 0x69, 0x4e, 0x6a,
59 | 0x10, 0xd8, 0x10, 0x27, 0x15, 0x2e, 0xbe, 0xe4, 0xfc, 0x5c, 0x24, 0x05, 0x4e, 0x02, 0x8e, 0x08,
60 | 0x67, 0x07, 0x80, 0x4c, 0x0e, 0x60, 0x4c, 0x62, 0x03, 0x5b, 0x61, 0x0c, 0x08, 0x00, 0x00, 0xff,
61 | 0xff, 0x4f, 0xd1, 0x89, 0x83, 0xde, 0x00, 0x00, 0x00,
62 | }
63 |
--------------------------------------------------------------------------------
/archive/src/proto/google/api/annotations.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go. DO NOT EDIT.
2 | // source: google/api/annotations.proto
3 |
4 | /*
5 | Package google_api is a generated protocol buffer package.
6 |
7 | It is generated from these files:
8 | google/api/annotations.proto
9 | google/api/http.proto
10 |
11 | It has these top-level messages:
12 | Http
13 | HttpRule
14 | CustomHttpPattern
15 | */
16 | package google_api
17 |
18 | import proto "github.com/golang/protobuf/proto"
19 | import fmt "fmt"
20 | import math "math"
21 | import google_protobuf "github.com/golang/protobuf/protoc-gen-go/descriptor"
22 |
23 | // Reference imports to suppress errors if they are not otherwise used.
24 | var _ = proto.Marshal
25 | var _ = fmt.Errorf
26 | var _ = math.Inf
27 |
28 | // This is a compile-time assertion to ensure that this generated file
29 | // is compatible with the proto package it is being compiled against.
30 | // A compilation error at this line likely means your copy of the
31 | // proto package needs to be updated.
32 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
33 |
34 | var E_Http = &proto.ExtensionDesc{
35 | ExtendedType: (*google_protobuf.MethodOptions)(nil),
36 | ExtensionType: (*HttpRule)(nil),
37 | Field: 72295728,
38 | Name: "google.api.http",
39 | Tag: "bytes,72295728,opt,name=http",
40 | Filename: "google/api/annotations.proto",
41 | }
42 |
43 | func init() {
44 | proto.RegisterExtension(E_Http)
45 | }
46 |
47 | func init() { proto.RegisterFile("google/api/annotations.proto", fileDescriptor0) }
48 |
49 | var fileDescriptor0 = []byte{
50 | // 169 bytes of a gzipped FileDescriptorProto
51 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x49, 0xcf, 0xcf, 0x4f,
52 | 0xcf, 0x49, 0xd5, 0x4f, 0x2c, 0xc8, 0xd4, 0x4f, 0xcc, 0xcb, 0xcb, 0x2f, 0x49, 0x2c, 0xc9, 0xcc,
53 | 0xcf, 0x2b, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x82, 0xc8, 0xea, 0x25, 0x16, 0x64,
54 | 0x4a, 0x89, 0x22, 0xa9, 0xcc, 0x28, 0x29, 0x29, 0x80, 0x28, 0x91, 0x52, 0x80, 0x0a, 0x83, 0x79,
55 | 0x49, 0xa5, 0x69, 0xfa, 0x29, 0xa9, 0xc5, 0xc9, 0x45, 0x99, 0x05, 0x25, 0xf9, 0x45, 0x10, 0x15,
56 | 0x56, 0xde, 0x5c, 0x2c, 0x20, 0xf5, 0x42, 0x72, 0x7a, 0x50, 0xd3, 0x60, 0x4a, 0xf5, 0x7c, 0x53,
57 | 0x4b, 0x32, 0xf2, 0x53, 0xfc, 0x0b, 0xc0, 0x56, 0x4a, 0x6c, 0x38, 0xb5, 0x47, 0x49, 0x81, 0x51,
58 | 0x83, 0xdb, 0x48, 0x44, 0x0f, 0x61, 0xad, 0x9e, 0x47, 0x49, 0x49, 0x41, 0x50, 0x69, 0x4e, 0x6a,
59 | 0x10, 0xd8, 0x10, 0x27, 0x15, 0x2e, 0xbe, 0xe4, 0xfc, 0x5c, 0x24, 0x05, 0x4e, 0x02, 0x8e, 0x08,
60 | 0x67, 0x07, 0x80, 0x4c, 0x0e, 0x60, 0x4c, 0x62, 0x03, 0x5b, 0x61, 0x0c, 0x08, 0x00, 0x00, 0xff,
61 | 0xff, 0x4f, 0xd1, 0x89, 0x83, 0xde, 0x00, 0x00, 0x00,
62 | }
63 |
--------------------------------------------------------------------------------
/archive/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Go gRPC Tutorial -- Jergoo
6 |
7 |
8 |
9 |
10 |
11 |
20 |
21 |
22 | Please wait...
23 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/src/ping/client.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "io"
6 | "log"
7 | "time"
8 |
9 | "google.golang.org/grpc"
10 |
11 | pb "github.com/jergoo/go-grpc-tutorial/protos/ping" // 引入编译生成的包
12 | )
13 |
14 | // Ping 单次请求-响应模式
15 | func Ping() {
16 | conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure())
17 | if err != nil {
18 | log.Fatal(err)
19 | }
20 | defer conn.Close()
21 |
22 | // 实例化客户端并调用
23 | client := pb.NewPingPongClient(conn)
24 | res, err := client.Ping(context.Background(), &pb.PingRequest{Value: "ping"})
25 | if err != nil {
26 | log.Fatal(err)
27 | }
28 | log.Println(res.Value)
29 | }
30 |
31 | // MultiPong 服务端流模式
32 | func MultiPong() {
33 | conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure())
34 | if err != nil {
35 | log.Fatal(err)
36 | }
37 | defer conn.Close()
38 |
39 | // 实例化客户端并调用
40 | client := pb.NewPingPongClient(conn)
41 | // 获得对 stream 对象的引用
42 | stream, err := client.MultiPong(context.Background(), &pb.PingRequest{Value: "ping"})
43 | if err != nil {
44 | log.Fatal(err)
45 | }
46 |
47 | // 循环接收数据流
48 | for {
49 | msg, err := stream.Recv()
50 | if err != nil {
51 | if err == io.EOF {
52 | break
53 | }
54 | log.Fatal(err)
55 | }
56 | log.Println(msg.Value)
57 | }
58 | }
59 |
60 | // MultiPing 客户端流模式
61 | func MultiPing() {
62 | conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure())
63 | if err != nil {
64 | log.Fatal(err)
65 | }
66 | defer conn.Close()
67 |
68 | // 实例化客户端并调用
69 | client := pb.NewPingPongClient(conn)
70 | stream, err := client.MultiPing(context.Background())
71 | if err != nil {
72 | log.Fatal(err)
73 | }
74 |
75 | // 发送数据
76 | for i := 0; i < 5; i++ {
77 | data := &pb.PingRequest{Value: "ping"}
78 | err = stream.Send(data)
79 | if err != nil {
80 | log.Fatal(err)
81 | }
82 | }
83 |
84 | // 发送结束并获取服务端响应
85 | res, err := stream.CloseAndRecv()
86 | if err != nil {
87 | log.Fatal(err)
88 | }
89 |
90 | log.Println(res.Value)
91 | }
92 |
93 | // MultiPingPong 双向流模式
94 | func MultiPingPong() {
95 | conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure())
96 | if err != nil {
97 | log.Fatal(err)
98 | }
99 | defer conn.Close()
100 |
101 | // 实例化客户端并调用
102 | client := pb.NewPingPongClient(conn)
103 | stream, err := client.MultiPingPong(context.Background())
104 | if err != nil {
105 | log.Fatal(err)
106 | }
107 |
108 | // 在另一个goroutine中接收数据
109 | c := make(chan struct{})
110 | go func(stream pb.PingPong_MultiPingPongClient, c chan struct{}) {
111 | defer func() {
112 | c <- struct{}{}
113 | }()
114 | for {
115 | msg, err := stream.Recv()
116 | if err != nil {
117 | if err == io.EOF {
118 | break
119 | }
120 | log.Fatal(err)
121 | }
122 | log.Printf("recv:%s\n", msg.Value)
123 | }
124 | }(stream, c)
125 |
126 | // 发送数据
127 | for i := 0; i < 6; i++ {
128 | data := &pb.PingRequest{Value: "ping"}
129 | err = stream.Send(data)
130 | if err != nil {
131 | log.Fatal(err)
132 | }
133 | log.Printf("send:%s\n", data.Value)
134 | time.Sleep(500 * time.Millisecond)
135 | }
136 |
137 | // 结束发送
138 | stream.CloseSend()
139 | // 等待接收完成
140 | <-c
141 | }
142 |
--------------------------------------------------------------------------------
/docs/assets/icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/docs/assets/icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/archive/docs/_media/icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/archive/docs/c1/protobuf-go.md:
--------------------------------------------------------------------------------
1 | # Protobuf⇢Go转换
2 |
3 | 这里使用一个测试文件对照说明常用结构的protobuf到golang的转换。只说明关键部分代码,详细内容请查看完整文件。示例文件在`proto/test`目录下。
4 |
5 |
6 | ## Package
7 |
8 | 在proto文件中使用`package`关键字声明包名,默认转换成go中的包名与此一致,如果需要指定不一样的包名,可以使用`go_package`选项:
9 |
10 | ```protobuf
11 | package test;
12 | option go_package="test";
13 | ```
14 |
15 | ## Message
16 |
17 | proto中的`message`对应go中的`struct`,全部使用驼峰命名规则。嵌套定义的`message`,`enum`转换为go之后,名称变为`Parent_Child`结构。
18 |
19 | 示例proto:
20 |
21 | ```protobuf
22 | // Test 测试
23 | message Test {
24 | int32 age = 1;
25 | int64 count = 2;
26 | double money = 3;
27 | float score = 4;
28 | string name = 5;
29 | bool fat = 6;
30 | bytes char = 7;
31 | // Status 枚举状态
32 | enum Status {
33 | OK = 0;
34 | FAIL = 1;
35 | }
36 | Status status = 8;
37 | // Child 子结构
38 | message Child {
39 | string sex = 1;
40 | }
41 | Child child = 9;
42 | map dict = 10;
43 | }
44 | ```
45 |
46 | 转换结果:
47 |
48 | ```go
49 | // Status 枚举状态
50 | type Test_Status int32
51 |
52 | const (
53 | Test_OK Test_Status = 0
54 | Test_FAIL Test_Status = 1
55 | )
56 |
57 | // Test 测试
58 | type Test struct {
59 | Age int32 `protobuf:"varint,1,opt,name=age" json:"age,omitempty"`
60 | Count int64 `protobuf:"varint,2,opt,name=count" json:"count,omitempty"`
61 | Money float64 `protobuf:"fixed64,3,opt,name=money" json:"money,omitempty"`
62 | Score float32 `protobuf:"fixed32,4,opt,name=score" json:"score,omitempty"`
63 | Name string `protobuf:"bytes,5,opt,name=name" json:"name,omitempty"`
64 | Fat bool `protobuf:"varint,6,opt,name=fat" json:"fat,omitempty"`
65 | Char []byte `protobuf:"bytes,7,opt,name=char,proto3" json:"char,omitempty"`
66 | Status Test_Status `protobuf:"varint,8,opt,name=status,enum=test.Test_Status" json:"status,omitempty"`
67 | Child *Test_Child `protobuf:"bytes,9,opt,name=child" json:"child,omitempty"`
68 | Dict map[string]string `protobuf:"bytes,10,rep,name=dict" json:"dict,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
69 | }
70 |
71 | // Child 子结构
72 | type Test_Child struct {
73 | Sex string `protobuf:"bytes,1,opt,name=sex" json:"sex,omitempty"`
74 | }
75 | ```
76 | 除了会生成对应的结构外,还会有些工具方法,如字段的getter:
77 |
78 | ```go
79 | func (m *Test) GetAge() int32 {
80 | if m != nil {
81 | return m.Age
82 | }
83 | return 0
84 | }
85 | ```
86 |
87 | 枚举类型会生成对应名称的常量,同时会有两个map方便使用:
88 |
89 | ```go
90 | var Test_Status_name = map[int32]string{
91 | 0: "OK",
92 | 1: "FAIL",
93 | }
94 | var Test_Status_value = map[string]int32{
95 | "OK": 0,
96 | "FAIL": 1,
97 | }
98 | ```
99 |
100 | ## Service
101 |
102 | 定义一个简单的Service,`TestService`有一个方法`Test`,接收一个`Request`参数,返回`Response`:
103 |
104 | ```protobuf
105 | // TestService 测试服务
106 | service TestService {
107 | // Test 测试方法
108 | rpc Test(Request) returns (Response) {};
109 | }
110 |
111 | // Request 请求结构
112 | message Request {
113 | string name = 1;
114 | }
115 |
116 | // Response 响应结构
117 | message Response {
118 | string message = 1;
119 | }
120 | ```
121 |
122 | 转换结果:
123 |
124 | ```go
125 | // 客户端接口
126 | type TestServiceClient interface {
127 | // Test 测试方法
128 | Test(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
129 | }
130 |
131 | // 服务端接口
132 | type TestServiceServer interface {
133 | // Test 测试方法
134 | Test(context.Context, *Request) (*Response, error)
135 | }
136 | ```
137 |
138 | 生成的go代码中包含该Service定义的接口,客户端接口已经自动实现了,直接供客户端使用者调用,服务端接口需要由服务提供方实现。
139 |
--------------------------------------------------------------------------------
/archive/src/hello_http_2/server/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "crypto/tls"
5 | "io/ioutil"
6 | "net"
7 | "net/http"
8 | "strings"
9 |
10 | "github.com/grpc-ecosystem/grpc-gateway/runtime"
11 | pb "github.com/jergoo/go-grpc-example/proto/hello_http"
12 | "golang.org/x/net/context"
13 | "golang.org/x/net/http2"
14 | "google.golang.org/grpc"
15 | "google.golang.org/grpc/credentials"
16 | "google.golang.org/grpc/grpclog"
17 | )
18 |
19 | // 定义helloHTTPService并实现约定的接口
20 | type helloHTTPService struct{}
21 |
22 | // HelloHTTPService Hello HTTP服务
23 | var HelloHTTPService = helloHTTPService{}
24 |
25 | // SayHello 实现Hello服务接口
26 | func (h helloHTTPService) SayHello(ctx context.Context, in *pb.HelloHTTPRequest) (*pb.HelloHTTPResponse, error) {
27 | resp := new(pb.HelloHTTPResponse)
28 | resp.Message = "Hello " + in.Name + "."
29 |
30 | return resp, nil
31 | }
32 |
33 | func main() {
34 | endpoint := "127.0.0.1:50052"
35 | conn, err := net.Listen("tcp", endpoint)
36 | if err != nil {
37 | grpclog.Fatalf("TCP Listen err:%v\n", err)
38 | }
39 |
40 | // grpc server
41 | creds, err := credentials.NewServerTLSFromFile("../../keys/server.pem", "../../keys/server.key")
42 | if err != nil {
43 | grpclog.Fatalf("Failed to create server TLS credentials %v", err)
44 | }
45 | grpcServer := grpc.NewServer(grpc.Creds(creds))
46 | pb.RegisterHelloHTTPServer(grpcServer, HelloHTTPService)
47 |
48 | // gw server
49 | ctx := context.Background()
50 | dcreds, err := credentials.NewClientTLSFromFile("../../keys/server.pem", "server name")
51 | if err != nil {
52 | grpclog.Fatalf("Failed to create client TLS credentials %v", err)
53 | }
54 | dopts := []grpc.DialOption{grpc.WithTransportCredentials(dcreds)}
55 | gwmux := runtime.NewServeMux()
56 | if err = pb.RegisterHelloHTTPHandlerFromEndpoint(ctx, gwmux, endpoint, dopts); err != nil {
57 | grpclog.Fatalf("Failed to register gw server: %v\n", err)
58 | }
59 |
60 | // http服务
61 | mux := http.NewServeMux()
62 | mux.Handle("/", gwmux)
63 |
64 | srv := &http.Server{
65 | Addr: endpoint,
66 | Handler: grpcHandlerFunc(grpcServer, mux),
67 | TLSConfig: getTLSConfig(),
68 | }
69 |
70 | grpclog.Infof("gRPC and https listen on: %s\n", endpoint)
71 |
72 | if err = srv.Serve(tls.NewListener(conn, srv.TLSConfig)); err != nil {
73 | grpclog.Fatal("ListenAndServe: ", err)
74 | }
75 |
76 | return
77 | }
78 |
79 | func getTLSConfig() *tls.Config {
80 | cert, _ := ioutil.ReadFile("../../keys/server.pem")
81 | key, _ := ioutil.ReadFile("../../keys/server.key")
82 | var demoKeyPair *tls.Certificate
83 | pair, err := tls.X509KeyPair(cert, key)
84 | if err != nil {
85 | grpclog.Fatalf("TLS KeyPair err: %v\n", err)
86 | }
87 | demoKeyPair = &pair
88 | return &tls.Config{
89 | Certificates: []tls.Certificate{*demoKeyPair},
90 | NextProtos: []string{http2.NextProtoTLS}, // HTTP2 TLS支持
91 | }
92 | }
93 |
94 | // grpcHandlerFunc returns an http.Handler that delegates to grpcServer on incoming gRPC
95 | // connections or otherHandler otherwise. Copied from cockroachdb.
96 | func grpcHandlerFunc(grpcServer *grpc.Server, otherHandler http.Handler) http.Handler {
97 | if otherHandler == nil {
98 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
99 | grpcServer.ServeHTTP(w, r)
100 | })
101 | }
102 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
103 | if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
104 | grpcServer.ServeHTTP(w, r)
105 | } else {
106 | otherHandler.ServeHTTP(w, r)
107 | }
108 | })
109 | }
110 |
--------------------------------------------------------------------------------
/src/docs/basic/hello-grpc.md:
--------------------------------------------------------------------------------
1 | # Hello gRPC
2 |
3 | ---
4 |
5 | 从一个简单的示例说明 go gRPC 的基本使用流程,实现一个 PingPong 服务,客户端发送 ping 请求,服务端返回 pong 响应。
6 |
7 | **源码目录:**
8 |
9 | ```
10 | |—- src/
11 | |-- ping/
12 | |—— client.go // 客户端
13 | |—— server.go // 服务端
14 | |—- protos/ping/
15 | |—— ping.proto // protobuf描述文件
16 | |—— ping.pb.go // protoc编译生成
17 | |-- ping_grpc.pb.go // protoc编译生成
18 | ```
19 |
20 | ## 编写 protobuf 文件
21 |
22 | ```protobuf
23 | // src/protos/ping/ping.proto
24 | syntax = "proto3"; // 指定proto版本
25 | package protos; // 指定包名
26 |
27 | // 指定go包路径
28 | option go_package = "protos/ping";
29 |
30 | // 定义PingPong服务
31 | service PingPong {
32 | // Ping 发送 ping 请求,接收 pong 响应
33 | rpc Ping(PingRequest) returns (PongResponse);
34 | }
35 |
36 | // PingRequest 请求结构
37 | message PingRequest {
38 | string value = 1; // value字段为string类型
39 | }
40 |
41 | // PongResponse 响应结构
42 | message PongResponse {
43 | string value = 1; // value字段为string类型
44 | }
45 | ```
46 |
47 | 定义了一个名为 `PingPong` 的 service,包含一个 `Ping` 方法,同时声明了 `PingRequest` 和 `PongResponse` 消息结构用于请求和响应。客户端使用 `PingRequest` 参数调用 `Ping` 方法请求服务端,服务端响应 `PongResponse` 消息,一个基本的服务就定义好了。
48 |
49 | ## 编译 protobuf 文件
50 |
51 | ```sh
52 | $ cd src
53 | $ protoc --go_out=. --go-grpc_out=. ./protos/ping/ping.proto
54 | ```
55 | 在src目录执行编译命令,会在目录 `src/protos/ping` 内生成两个文件 `ping.pb.go` 和 `ping_grpc.pb.go`。可以大概看一下这两个文件的内容,`ping.pb.go` 包含了之前定义的两个message相关的结构,`ping_grpc.pb.go` 包含了定义的service相关的客户端和服务端接口,**不要修改这两个文件的内容**。
56 |
57 | ## 实现服务端接口
58 |
59 | ```go
60 | // src/ping/server.go
61 | package main
62 |
63 | import (
64 | "context"
65 | "log"
66 | "net"
67 |
68 | "google.golang.org/grpc"
69 |
70 | pb "github.com/jergoo/go-grpc-tutorial/protos/ping" // 引入编译生成的包
71 | )
72 |
73 | // PingPongServer 实现 pb.PingPongServer 接口
74 | type PingPongServer struct {
75 | pb.UnimplementedPingPongServer
76 | }
77 |
78 | // Ping 单次请求-响应模式
79 | func (s *PingPongServer) Ping(ctx context.Context, req *pb.PingRequest) (*pb.PongResponse, error) {
80 | return &pb.PongResponse{Value: "pong"}, nil
81 | }
82 |
83 | // 启动server
84 | func main() {
85 | srv := grpc.NewServer()
86 | // 注册 PingPongServer
87 | pb.RegisterPingPongServer(srv, &PingPongServer{})
88 | lis, err := net.Listen("tcp", ":1234")
89 | if err != nil {
90 | log.Fatal(err)
91 | }
92 | log.Println("listen on 1234")
93 | srv.Serve(lis)
94 | }
95 | ```
96 |
97 | 服务端引入编译生成的包,定义一个 `PingPongServer` 用于实现约定的接口,接口描述可以查看 `ping_grpc.pb.go` 文件中的 `PingPongServer` 接口。实例化 grpc Server 并注册 `PingPongServer` 开始提供服务。
98 |
99 | ## 客户端调用
100 |
101 | ```go
102 | // src/ping/client.go
103 | package main
104 |
105 | import (
106 | "context"
107 | "log"
108 |
109 | "google.golang.org/grpc"
110 |
111 | pb "github.com/jergoo/go-grpc-tutorial/protos/ping" // 引入编译生成的包
112 | )
113 |
114 | // Ping 单次请求-响应模式
115 | func Ping() {
116 | conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure())
117 | if err != nil {
118 | log.Fatal(err)
119 | }
120 | defer conn.Close()
121 |
122 | // 实例化客户端并调用
123 | client := pb.NewPingPongClient(conn)
124 | res, err := client.Ping(context.Background(), &pb.PingRequest{Value: "ping"})
125 | if err != nil {
126 | log.Fatal(err)
127 | }
128 | log.Println(res.Value)
129 | }
130 | ```
131 |
132 | 客户端初始化连接,使用 `ping_grpc.pb.go` 中的 `PingPongClient` 实例调用 `Ping` 方法,即可向服务端发起请求并获取响应,就像调用本地方法一样。
133 |
134 | ---
135 |
136 | > 以上就是一个最基础的 gRPC 服务,使用非常简单,底层网络细节全部由 gRPC 处理,开发者只需要关注业务接口设计和实现,基本流程如下:
137 | > 1. 编写 protobuf 描述文件,定义消息结构和服务接口
138 | > 2. 编译 protobuf 文件,生成服务端和客户端接口代码
139 | > 3. 实现 `*_grpc.pb.go` 文件中描述的服务端接口
140 | > 4. 使用 `*_grpc.pb.go` 文件中的client调用服务
141 |
--------------------------------------------------------------------------------
/src/docs/advance/metadata.md:
--------------------------------------------------------------------------------
1 | # metadata
2 |
3 | ---
4 |
5 | 服务间使用 Http 相互调用时,经常会设置一些业务自定义 header 如时间戳、trace信息等,gRPC使用 HTTP/2 协议自然也是支持的,gRPC 通过 `google.golang.org/grpc/metadata` 包内的 `MD` 类型提供相关的功能接口。
6 |
7 | **源码目录:**
8 |
9 | ```
10 | |—- src/
11 | |-- metadata/
12 | |—— client.go // 客户端
13 | |—— server.go // 服务端
14 | |—- protos/ping/
15 | |—— ping.proto // protobuf描述文件
16 | |—— ping.pb.go // protoc编译生成
17 | |-- ping_grpc.pb.go // protoc编译生成
18 | ```
19 |
20 | ## 类型定义
21 |
22 | ```go
23 | // MD is a mapping from metadata keys to values. Users should use the following
24 | // two convenience functions New and Pairs to generate MD.
25 | type MD map[string][]string
26 | ```
27 |
28 | `metadata.MD` 类型的定义非常简单,可以像一个普通的 map 一样直接操作,同时 metadata 包里封装了很多工具方法供我们使用。
29 |
30 | ```go
31 | // 使用 New 方法创建
32 | md := metadata.New(map[string]string{"k1":"v1", "k2", "v2"})
33 |
34 | // 直接使用 make 创建
35 | md := make(metadata.MD)
36 |
37 | // 使用 Pairs 方法创建
38 | md := metadata.Pairs("k1", "v1-1", "k1", "v1-2")
39 |
40 | // 一些操作
41 | md.Set("key", "v1", "v2")
42 | md.Append("key", "v3")
43 | md.Delete("key")
44 | vals := md.Get("key")
45 |
46 | ```
47 |
48 | ## 发送与接收
49 | ### 客户端
50 |
51 | 客户端请求的 metadata 是通过设置 context 使用的,metadata 包提供了两个 context 相关的方法,设置好 context 后直接在调用 rpc 方法时传入即可,示例代码如下:
52 |
53 | ```go
54 | md := metadata.New(map[string]string{"k1":"v1", "k2", "v2"})
55 |
56 | // 使用 NewOutgoingContext 初始化一个新的 context
57 | ctx := metadata.NewOutgoingContext(context.Background(), md)
58 |
59 | // 使用 AppendToOutgoingContext 向 context 追加 metadata
60 | ctx = metadata.AppendToOutgoingContext(ctx, "k3", "v3")
61 |
62 | ```
63 |
64 | 客户端接收响应中的 metadata 需要区分普通 rpc 和 stream rpc,示例如下:
65 |
66 | ```go
67 | // 普通 rpc,使用 grpc.Header 方法包装为 CallOption
68 | var md metadata.MD
69 | res, err := client.Ping(ctx, &pb.PingRequest{Value: "ping"}, grpc.Header(&md))
70 |
71 | // stream rpc
72 | stream, err := client.MultiPong(context.Background(), &pb.PingRequest{Value: "ping"})
73 | if err != nil {
74 | log.Fatal(err)
75 | }
76 |
77 | // 通过 stream 对象的 Header 方法获取
78 | md, err := stream.Header()
79 | if err != nil {
80 | log.Fatal(err)
81 | }
82 | ```
83 |
84 | ### 服务端
85 |
86 | 对应客户端请求的 metadata 是使用 context 设置的,那么服务端在接收时自然也是从 context 中读取,metadata 包中的 `FromIncommingContext` 方法就是用来读取 context 中的 metadata数据的,示例如下:
87 |
88 | ```go
89 | // unary rpc
90 | func (s *PingPongServer) Ping(ctx context.Context, req *pb.PingRequest) (*pb.PongResponse, error) {
91 | // 读取请求metadata
92 | md, ok := metadata.FromIncomingContext(ctx)
93 | if ok {
94 | log.Printf("Got md: %v", md)
95 | }
96 |
97 | ...
98 |
99 | // stream rpc
100 | func (s *PingPongServer) MultiPingPong(stream pb.PingPong_MultiPingPongServer) error {
101 | md, ok := metadata.FromIncomingContext(stream.Context())
102 | if ok {
103 | log.Printf("Got md: %v", md)
104 | }
105 |
106 | ...
107 | ```
108 |
109 | 服务端设置响应的 metadata 也非常简单,只需要调用封装好的 `SetHeader` 或 `SendHeader` 方法即可,示例如下:
110 |
111 | ```go
112 | // unary rpc
113 | func (s *PingPongServer) Ping(ctx context.Context, req *pb.PingRequest) (*pb.PongResponse, error) {
114 | // 读取请求metadata
115 | md, ok := metadata.FromIncomingContext(ctx)
116 | if ok {
117 | log.Printf("Got md: %v", md)
118 | }
119 |
120 | // SetHeader设置响应 metadata
121 | grpc.SetHeader(ctx, metadata.New(map[string]string{"rkey": "rval"}))
122 | // 注意 SendHeader 只能调用一次
123 | // grpc.SendHeader(ctx, metadata.New(map[string]string{"rkey": "rval"}))
124 |
125 | ....
126 |
127 | // stream rpc, 调用 stream 的 SetHeader 方法
128 | func (s *PingPongServer) MultiPong(req *pb.PingRequest, stream pb.PingPong_MultiPongServer) error {
129 | stream.SetHeader(metadata.New(map[string]string{"rkey": "rval"}))
130 |
131 | // 注意 SendHeader 只能调用一次
132 | // stream.SendHeader(metadata.New(map[string]string{"rkey": "rval"}))
133 | ```
134 | ---
135 |
136 |
--------------------------------------------------------------------------------
/archive/src/proto/hello_http/hello_http.pb.gw.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
2 | // source: hello_http/hello_http.proto
3 |
4 | /*
5 | Package hello_http is a reverse proxy.
6 |
7 | It translates gRPC into RESTful JSON APIs.
8 | */
9 | package hello_http
10 |
11 | import (
12 | "io"
13 | "net/http"
14 |
15 | "github.com/golang/protobuf/proto"
16 | "github.com/grpc-ecosystem/grpc-gateway/runtime"
17 | "github.com/grpc-ecosystem/grpc-gateway/utilities"
18 | "golang.org/x/net/context"
19 | "google.golang.org/grpc"
20 | "google.golang.org/grpc/codes"
21 | "google.golang.org/grpc/grpclog"
22 | "google.golang.org/grpc/status"
23 | )
24 |
25 | var _ codes.Code
26 | var _ io.Reader
27 | var _ status.Status
28 | var _ = runtime.String
29 | var _ = utilities.NewDoubleArray
30 |
31 | func request_HelloHTTP_SayHello_0(ctx context.Context, marshaler runtime.Marshaler, client HelloHTTPClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
32 | var protoReq HelloHTTPRequest
33 | var metadata runtime.ServerMetadata
34 |
35 | if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil {
36 | return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
37 | }
38 |
39 | msg, err := client.SayHello(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
40 | return msg, metadata, err
41 |
42 | }
43 |
44 | // RegisterHelloHTTPHandlerFromEndpoint is same as RegisterHelloHTTPHandler but
45 | // automatically dials to "endpoint" and closes the connection when "ctx" gets done.
46 | func RegisterHelloHTTPHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
47 | conn, err := grpc.Dial(endpoint, opts...)
48 | if err != nil {
49 | return err
50 | }
51 | defer func() {
52 | if err != nil {
53 | if cerr := conn.Close(); cerr != nil {
54 | grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
55 | }
56 | return
57 | }
58 | go func() {
59 | <-ctx.Done()
60 | if cerr := conn.Close(); cerr != nil {
61 | grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
62 | }
63 | }()
64 | }()
65 |
66 | return RegisterHelloHTTPHandler(ctx, mux, conn)
67 | }
68 |
69 | // RegisterHelloHTTPHandler registers the http handlers for service HelloHTTP to "mux".
70 | // The handlers forward requests to the grpc endpoint over "conn".
71 | func RegisterHelloHTTPHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
72 | client := NewHelloHTTPClient(conn)
73 |
74 | mux.Handle("POST", pattern_HelloHTTP_SayHello_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
75 | ctx, cancel := context.WithCancel(ctx)
76 | defer cancel()
77 | if cn, ok := w.(http.CloseNotifier); ok {
78 | go func(done <-chan struct{}, closed <-chan bool) {
79 | select {
80 | case <-done:
81 | case <-closed:
82 | cancel()
83 | }
84 | }(ctx.Done(), cn.CloseNotify())
85 | }
86 | inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
87 | rctx, err := runtime.AnnotateContext(ctx, mux, req)
88 | if err != nil {
89 | runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
90 | return
91 | }
92 | resp, md, err := request_HelloHTTP_SayHello_0(rctx, inboundMarshaler, client, req, pathParams)
93 | ctx = runtime.NewServerMetadataContext(ctx, md)
94 | if err != nil {
95 | runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
96 | return
97 | }
98 |
99 | forward_HelloHTTP_SayHello_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
100 |
101 | })
102 |
103 | return nil
104 | }
105 |
106 | var (
107 | pattern_HelloHTTP_SayHello_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"example", "echo"}, ""))
108 | )
109 |
110 | var (
111 | forward_HelloHTTP_SayHello_0 = runtime.ForwardResponseMessage
112 | )
113 |
--------------------------------------------------------------------------------
/docs/fonts/fonts.css:
--------------------------------------------------------------------------------
1 | /* Open Sans is licensed under the Apache License, Version 2.0. See http://www.apache.org/licenses/LICENSE-2.0 */
2 | /* Source Code Pro is under the Open Font License. See https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL */
3 |
4 | /* open-sans-300 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */
5 | @font-face {
6 | font-family: 'Open Sans';
7 | font-style: normal;
8 | font-weight: 300;
9 | src: local('Open Sans Light'), local('OpenSans-Light'),
10 | url('open-sans-v17-all-charsets-300.woff2') format('woff2');
11 | }
12 |
13 | /* open-sans-300italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */
14 | @font-face {
15 | font-family: 'Open Sans';
16 | font-style: italic;
17 | font-weight: 300;
18 | src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'),
19 | url('open-sans-v17-all-charsets-300italic.woff2') format('woff2');
20 | }
21 |
22 | /* open-sans-regular - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */
23 | @font-face {
24 | font-family: 'Open Sans';
25 | font-style: normal;
26 | font-weight: 400;
27 | src: local('Open Sans Regular'), local('OpenSans-Regular'),
28 | url('open-sans-v17-all-charsets-regular.woff2') format('woff2');
29 | }
30 |
31 | /* open-sans-italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */
32 | @font-face {
33 | font-family: 'Open Sans';
34 | font-style: italic;
35 | font-weight: 400;
36 | src: local('Open Sans Italic'), local('OpenSans-Italic'),
37 | url('open-sans-v17-all-charsets-italic.woff2') format('woff2');
38 | }
39 |
40 | /* open-sans-600 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */
41 | @font-face {
42 | font-family: 'Open Sans';
43 | font-style: normal;
44 | font-weight: 600;
45 | src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'),
46 | url('open-sans-v17-all-charsets-600.woff2') format('woff2');
47 | }
48 |
49 | /* open-sans-600italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */
50 | @font-face {
51 | font-family: 'Open Sans';
52 | font-style: italic;
53 | font-weight: 600;
54 | src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'),
55 | url('open-sans-v17-all-charsets-600italic.woff2') format('woff2');
56 | }
57 |
58 | /* open-sans-700 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */
59 | @font-face {
60 | font-family: 'Open Sans';
61 | font-style: normal;
62 | font-weight: 700;
63 | src: local('Open Sans Bold'), local('OpenSans-Bold'),
64 | url('open-sans-v17-all-charsets-700.woff2') format('woff2');
65 | }
66 |
67 | /* open-sans-700italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */
68 | @font-face {
69 | font-family: 'Open Sans';
70 | font-style: italic;
71 | font-weight: 700;
72 | src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'),
73 | url('open-sans-v17-all-charsets-700italic.woff2') format('woff2');
74 | }
75 |
76 | /* open-sans-800 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */
77 | @font-face {
78 | font-family: 'Open Sans';
79 | font-style: normal;
80 | font-weight: 800;
81 | src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'),
82 | url('open-sans-v17-all-charsets-800.woff2') format('woff2');
83 | }
84 |
85 | /* open-sans-800italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */
86 | @font-face {
87 | font-family: 'Open Sans';
88 | font-style: italic;
89 | font-weight: 800;
90 | src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'),
91 | url('open-sans-v17-all-charsets-800italic.woff2') format('woff2');
92 | }
93 |
94 | /* source-code-pro-500 - latin_vietnamese_latin-ext_greek_cyrillic-ext_cyrillic */
95 | @font-face {
96 | font-family: 'Source Code Pro';
97 | font-style: normal;
98 | font-weight: 500;
99 | src: url('source-code-pro-v11-all-charsets-500.woff2') format('woff2');
100 | }
101 |
--------------------------------------------------------------------------------
/src/protos/google/protobuf/struct.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | syntax = "proto3";
32 |
33 | package google.protobuf;
34 |
35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
36 | option cc_enable_arenas = true;
37 | option go_package = "google.golang.org/protobuf/types/known/structpb";
38 | option java_package = "com.google.protobuf";
39 | option java_outer_classname = "StructProto";
40 | option java_multiple_files = true;
41 | option objc_class_prefix = "GPB";
42 |
43 | // `Struct` represents a structured data value, consisting of fields
44 | // which map to dynamically typed values. In some languages, `Struct`
45 | // might be supported by a native representation. For example, in
46 | // scripting languages like JS a struct is represented as an
47 | // object. The details of that representation are described together
48 | // with the proto support for the language.
49 | //
50 | // The JSON representation for `Struct` is JSON object.
51 | message Struct {
52 | // Unordered map of dynamically typed values.
53 | map fields = 1;
54 | }
55 |
56 | // `Value` represents a dynamically typed value which can be either
57 | // null, a number, a string, a boolean, a recursive struct value, or a
58 | // list of values. A producer of value is expected to set one of these
59 | // variants. Absence of any variant indicates an error.
60 | //
61 | // The JSON representation for `Value` is JSON value.
62 | message Value {
63 | // The kind of value.
64 | oneof kind {
65 | // Represents a null value.
66 | NullValue null_value = 1;
67 | // Represents a double value.
68 | double number_value = 2;
69 | // Represents a string value.
70 | string string_value = 3;
71 | // Represents a boolean value.
72 | bool bool_value = 4;
73 | // Represents a structured value.
74 | Struct struct_value = 5;
75 | // Represents a repeated `Value`.
76 | ListValue list_value = 6;
77 | }
78 | }
79 |
80 | // `NullValue` is a singleton enumeration to represent the null value for the
81 | // `Value` type union.
82 | //
83 | // The JSON representation for `NullValue` is JSON `null`.
84 | enum NullValue {
85 | // Null value.
86 | NULL_VALUE = 0;
87 | }
88 |
89 | // `ListValue` is a wrapper around a repeated field of values.
90 | //
91 | // The JSON representation for `ListValue` is JSON array.
92 | message ListValue {
93 | // Repeated field of dynamically typed values.
94 | repeated Value values = 1;
95 | }
96 |
--------------------------------------------------------------------------------
/src/interceptor/server.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "io"
7 | "log"
8 | "net"
9 |
10 | "google.golang.org/grpc"
11 |
12 | pb "github.com/jergoo/go-grpc-tutorial/protos/ping" // 引入编译生成的包
13 | )
14 |
15 | // PingPongServer 实现 pb.PingPongServer 接口
16 | type PingPongServer struct {
17 | pb.UnimplementedPingPongServer // 兼容性需要,避免未实现server接口全部方法
18 | }
19 |
20 | // Ping 单次请求-响应模式
21 | func (s *PingPongServer) Ping(ctx context.Context, req *pb.PingRequest) (*pb.PongResponse, error) {
22 | return &pb.PongResponse{Value: "pong"}, nil
23 | }
24 |
25 | // MultiPong 服务端流模式
26 | func (s *PingPongServer) MultiPong(req *pb.PingRequest, stream pb.PingPong_MultiPongServer) error {
27 | for i := 0; i < 10; i++ {
28 | data := &pb.PongResponse{Value: "pong"}
29 | // 发送消息
30 | err := stream.Send(data)
31 | if err != nil {
32 | return err
33 | }
34 | }
35 | return nil
36 | }
37 |
38 | // MultiPing 客户端流模式
39 | func (s *PingPongServer) MultiPing(stream pb.PingPong_MultiPingServer) error {
40 | msgs := []string{}
41 | for {
42 | // 提前结束接收消息
43 | if len(msgs) > 5 {
44 | return stream.SendAndClose(&pb.PongResponse{Value: "ping enough, max 5"})
45 | }
46 |
47 | msg, err := stream.Recv()
48 | if err != nil {
49 | // 客户端消息结束,返回响应信息
50 | if err == io.EOF {
51 | return stream.SendAndClose(&pb.PongResponse{Value: fmt.Sprintf("got %d ping", len(msgs))})
52 | }
53 | return err
54 | }
55 | msgs = append(msgs, msg.Value)
56 | }
57 | }
58 |
59 | // MultiPingPong 双向流模式
60 | func (s *PingPongServer) MultiPingPong(stream pb.PingPong_MultiPingPongServer) error {
61 | msgs := []string{}
62 | for {
63 | // 接收消息
64 | msg, err := stream.Recv()
65 | if err != nil {
66 | if err == io.EOF {
67 | break
68 | }
69 | return err
70 | }
71 | msgs = append(msgs, msg.Value)
72 |
73 | // 每收到两个消息响应一次
74 | if len(msgs)%2 == 0 {
75 | err = stream.Send(&pb.PongResponse{Value: "pong"})
76 | if err != nil {
77 | return err
78 | }
79 | }
80 | }
81 | return nil
82 | }
83 |
84 | // type UnaryServerInterceptor func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (resp interface{}, err error)
85 |
86 | // 服务端拦截器 - 记录请求和响应日志
87 | func serverUnaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
88 | // 前置逻辑
89 | log.Printf("[Server Interceptor] accept request: %s", info.FullMethod)
90 |
91 | // 处理请求
92 | response, err := handler(ctx, req)
93 |
94 | // 后置逻辑
95 | log.Printf("[Server Interceptor] response: %s", response)
96 |
97 | return response, err
98 | }
99 |
100 | // type StreamServerInterceptor func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error
101 |
102 | // 服务端拦截器 - 记录stream请求和响应日志
103 | func serverStreamInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
104 | // 前置逻辑
105 | log.Printf("[Server Stream Interceptor] accept request: %s", info.FullMethod)
106 |
107 | // 处理请求,使用自定义 ServerStream
108 | err := handler(srv, &customServerStream{ss})
109 | return err
110 | }
111 |
112 | type customServerStream struct {
113 | grpc.ServerStream
114 | }
115 |
116 | func (s *customServerStream) SendMsg(m interface{}) error {
117 | log.Printf("[Server Stream Interceptor] send: %T", m)
118 | return s.ServerStream.SendMsg(m)
119 | }
120 |
121 | func (s *customServerStream) RecvMsg(m interface{}) error {
122 | log.Printf("[Server Stream Interceptor] recv: %T", m)
123 | return s.ServerStream.RecvMsg(m)
124 | }
125 |
126 | // 启动server
127 | func main() {
128 | // 以option的方式添加拦截器
129 | opts := []grpc.ServerOption{
130 | grpc.UnaryInterceptor(serverUnaryInterceptor),
131 | grpc.StreamInterceptor(serverStreamInterceptor),
132 | }
133 | srv := grpc.NewServer(opts...)
134 |
135 | // 注册 PingPongServer
136 | pb.RegisterPingPongServer(srv, &PingPongServer{})
137 | lis, err := net.Listen("tcp", ":1234")
138 | if err != nil {
139 | log.Fatal(err)
140 | }
141 | log.Println("listen on 1234")
142 | srv.Serve(lis)
143 | }
144 |
--------------------------------------------------------------------------------
/archive/docs/c2/hello-grpc.md:
--------------------------------------------------------------------------------
1 | # Hello gRPC
2 |
3 | 按照惯例,这里也从一个Hello项目开始,本项目定义了一个Hello Service,客户端发送包含字符串名字的请求,服务端返回Hello消息。
4 |
5 | **流程:**
6 |
7 | 1. 编写`.proto`描述文件
8 | 2. 编译生成`.pb.go`文件
9 | 3. 服务端实现约定的接口并提供服务
10 | 4. 客户端按照约定调用`.pb.go`文件中的方法请求服务
11 |
12 | **项目结构:**
13 |
14 | ```
15 | |—— hello/
16 | |—— client/
17 | |—— main.go // 客户端
18 | |—— server/
19 | |—— main.go // 服务端
20 | |—— proto/
21 | |—— hello/
22 | |—— hello.proto // proto描述文件
23 | |—— hello.pb.go // proto编译后文件
24 | ```
25 |
26 |
27 | **Step1:编写描述文件:hello.proto**
28 |
29 | ```protobuf
30 | syntax = "proto3"; // 指定proto版本
31 | package hello; // 指定默认包名
32 |
33 | // 指定golang包名
34 | option go_package = "hello";
35 |
36 | // 定义Hello服务
37 | service Hello {
38 | // 定义SayHello方法
39 | rpc SayHello(HelloRequest) returns (HelloResponse) {}
40 | }
41 |
42 | // HelloRequest 请求结构
43 | message HelloRequest {
44 | string name = 1;
45 | }
46 |
47 | // HelloResponse 响应结构
48 | message HelloResponse {
49 | string message = 1;
50 | }
51 | ```
52 |
53 | `hello.proto`文件中定义了一个Hello Service,该服务包含一个`SayHello`方法,同时声明了`HelloRequest`和`HelloResponse`消息结构用于请求和响应。客户端使用`HelloRequest`参数调用`SayHello`方法请求服务端,服务端响应`HelloResponse`消息。一个最简单的服务就定义好了。
54 |
55 |
56 | **Step2:编译生成`.pb.go`文件**
57 |
58 | ```sh
59 | $ cd proto/hello
60 |
61 | # 编译hello.proto
62 | $ protoc -I . --go_out=plugins=grpc:. ./hello.proto
63 | ```
64 | 在当前目录内生成的`hello.pb.go`文件,按照`.proto`文件中的说明,包含服务端接口`HelloServer`描述,客户端接口及实现`HelloClient`,及`HelloRequest`、`HelloResponse`结构体。
65 |
66 | > 注意:不要手动编辑该文件
67 |
68 |
69 | **Step3:实现服务端接口 `server/main.go`**
70 |
71 | ```go
72 | package main
73 |
74 | import (
75 | "fmt"
76 | "net"
77 |
78 | pb "github.com/jergoo/go-grpc-example/proto/hello" // 引入编译生成的包
79 | "golang.org/x/net/context"
80 | "google.golang.org/grpc"
81 | "google.golang.org/grpc/grpclog"
82 | )
83 |
84 | const (
85 | // Address gRPC服务地址
86 | Address = "127.0.0.1:50052"
87 | )
88 |
89 | // 定义helloService并实现约定的接口
90 | type helloService struct{}
91 |
92 | // HelloService Hello服务
93 | var HelloService = helloService{}
94 |
95 | // SayHello 实现Hello服务接口
96 | func (h helloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
97 | resp := new(pb.HelloResponse)
98 | resp.Message = fmt.Sprintf("Hello %s.", in.Name)
99 |
100 | return resp, nil
101 | }
102 |
103 | func main() {
104 | listen, err := net.Listen("tcp", Address)
105 | if err != nil {
106 | grpclog.Fatalf("Failed to listen: %v", err)
107 | }
108 |
109 | // 实例化grpc Server
110 | s := grpc.NewServer()
111 |
112 | // 注册HelloService
113 | pb.RegisterHelloServer(s, HelloService)
114 |
115 | grpclog.Println("Listen on " + Address)
116 | s.Serve(listen)
117 | }
118 |
119 | ```
120 |
121 | 服务端引入编译后的`proto`包,定义一个空结构用于实现约定的接口,接口描述可以查看`hello.pb.go`文件中的`HelloServer`接口描述。实例化grpc Server并注册HelloService,开始提供服务。
122 |
123 | 运行:
124 |
125 | ```sh
126 | $ go run main.go
127 | Listen on 127.0.0.1:50052 //服务端已开启并监听50052端口
128 | ```
129 |
130 | **Step4:实现客户端调用 `client/main.go`**
131 |
132 | ```go
133 | package main
134 |
135 | import (
136 | pb "github.com/jergoo/go-grpc-example/proto/hello" // 引入proto包
137 | "golang.org/x/net/context"
138 | "google.golang.org/grpc"
139 | "google.golang.org/grpc/grpclog"
140 | )
141 |
142 | const (
143 | // Address gRPC服务地址
144 | Address = "127.0.0.1:50052"
145 | )
146 |
147 | func main() {
148 | // 连接
149 | conn, err := grpc.Dial(Address, grpc.WithInsecure())
150 | if err != nil {
151 | grpclog.Fatalln(err)
152 | }
153 | defer conn.Close()
154 |
155 | // 初始化客户端
156 | c := pb.NewHelloClient(conn)
157 |
158 | // 调用方法
159 | req := &pb.HelloRequest{Name: "gRPC"}
160 | res, err := c.SayHello(context.Background(), req)
161 |
162 | if err != nil {
163 | grpclog.Fatalln(err)
164 | }
165 |
166 | grpclog.Println(res.Message)
167 | }
168 | ```
169 |
170 | 客户端初始化连接后直接调用`hello.pb.go`中实现的`SayHello`方法,即可向服务端发起请求,使用姿势就像调用本地方法一样。
171 |
172 |
173 | 运行:
174 |
175 | ```sh
176 | $ go run main.go
177 | Hello gRPC. // 接收到服务端响应
178 | ```
179 |
180 | 如果你收到了"Hello gRPC"的回复,恭喜你已经会使用github.com/jergoo/go-grpc-example/proto/hello了。
181 |
182 | > 建议到这里仔细看一看hello.pb.go文件中的内容,对比hello.proto文件,理解protobuf中的定义转换为golang后的结构。
183 |
--------------------------------------------------------------------------------
/src/protos/google/protobuf/wrappers.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | // Wrappers for primitive (non-message) types. These types are useful
32 | // for embedding primitives in the `google.protobuf.Any` type and for places
33 | // where we need to distinguish between the absence of a primitive
34 | // typed field and its default value.
35 | //
36 | // These wrappers have no meaningful use within repeated fields as they lack
37 | // the ability to detect presence on individual elements.
38 | // These wrappers have no meaningful use within a map or a oneof since
39 | // individual entries of a map or fields of a oneof can already detect presence.
40 |
41 | syntax = "proto3";
42 |
43 | package google.protobuf;
44 |
45 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
46 | option cc_enable_arenas = true;
47 | option go_package = "google.golang.org/protobuf/types/known/wrapperspb";
48 | option java_package = "com.google.protobuf";
49 | option java_outer_classname = "WrappersProto";
50 | option java_multiple_files = true;
51 | option objc_class_prefix = "GPB";
52 |
53 | // Wrapper message for `double`.
54 | //
55 | // The JSON representation for `DoubleValue` is JSON number.
56 | message DoubleValue {
57 | // The double value.
58 | double value = 1;
59 | }
60 |
61 | // Wrapper message for `float`.
62 | //
63 | // The JSON representation for `FloatValue` is JSON number.
64 | message FloatValue {
65 | // The float value.
66 | float value = 1;
67 | }
68 |
69 | // Wrapper message for `int64`.
70 | //
71 | // The JSON representation for `Int64Value` is JSON string.
72 | message Int64Value {
73 | // The int64 value.
74 | int64 value = 1;
75 | }
76 |
77 | // Wrapper message for `uint64`.
78 | //
79 | // The JSON representation for `UInt64Value` is JSON string.
80 | message UInt64Value {
81 | // The uint64 value.
82 | uint64 value = 1;
83 | }
84 |
85 | // Wrapper message for `int32`.
86 | //
87 | // The JSON representation for `Int32Value` is JSON number.
88 | message Int32Value {
89 | // The int32 value.
90 | int32 value = 1;
91 | }
92 |
93 | // Wrapper message for `uint32`.
94 | //
95 | // The JSON representation for `UInt32Value` is JSON number.
96 | message UInt32Value {
97 | // The uint32 value.
98 | uint32 value = 1;
99 | }
100 |
101 | // Wrapper message for `bool`.
102 | //
103 | // The JSON representation for `BoolValue` is JSON `true` and `false`.
104 | message BoolValue {
105 | // The bool value.
106 | bool value = 1;
107 | }
108 |
109 | // Wrapper message for `string`.
110 | //
111 | // The JSON representation for `StringValue` is JSON string.
112 | message StringValue {
113 | // The string value.
114 | string value = 1;
115 | }
116 |
117 | // Wrapper message for `bytes`.
118 | //
119 | // The JSON representation for `BytesValue` is JSON string.
120 | message BytesValue {
121 | // The bytes value.
122 | bytes value = 1;
123 | }
124 |
--------------------------------------------------------------------------------
/docs/assets/bar.svg:
--------------------------------------------------------------------------------
1 |
2 |
50 |
--------------------------------------------------------------------------------
/src/docs/assets/bar.svg:
--------------------------------------------------------------------------------
1 |
2 |
50 |
--------------------------------------------------------------------------------
/archive/docs/_media/bar.svg:
--------------------------------------------------------------------------------
1 |
2 |
50 |
--------------------------------------------------------------------------------
/docs/fonts/SOURCE-CODE-PRO-LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
2 |
3 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
4 | This license is copied below, and is also available with a FAQ at:
5 | http://scripts.sil.org/OFL
6 |
7 |
8 | -----------------------------------------------------------
9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10 | -----------------------------------------------------------
11 |
12 | PREAMBLE
13 | The goals of the Open Font License (OFL) are to stimulate worldwide
14 | development of collaborative font projects, to support the font creation
15 | efforts of academic and linguistic communities, and to provide a free and
16 | open framework in which fonts may be shared and improved in partnership
17 | with others.
18 |
19 | The OFL allows the licensed fonts to be used, studied, modified and
20 | redistributed freely as long as they are not sold by themselves. The
21 | fonts, including any derivative works, can be bundled, embedded,
22 | redistributed and/or sold with any software provided that any reserved
23 | names are not used by derivative works. The fonts and derivatives,
24 | however, cannot be released under any other type of license. The
25 | requirement for fonts to remain under this license does not apply
26 | to any document created using the fonts or their derivatives.
27 |
28 | DEFINITIONS
29 | "Font Software" refers to the set of files released by the Copyright
30 | Holder(s) under this license and clearly marked as such. This may
31 | include source files, build scripts and documentation.
32 |
33 | "Reserved Font Name" refers to any names specified as such after the
34 | copyright statement(s).
35 |
36 | "Original Version" refers to the collection of Font Software components as
37 | distributed by the Copyright Holder(s).
38 |
39 | "Modified Version" refers to any derivative made by adding to, deleting,
40 | or substituting -- in part or in whole -- any of the components of the
41 | Original Version, by changing formats or by porting the Font Software to a
42 | new environment.
43 |
44 | "Author" refers to any designer, engineer, programmer, technical
45 | writer or other person who contributed to the Font Software.
46 |
47 | PERMISSION & CONDITIONS
48 | Permission is hereby granted, free of charge, to any person obtaining
49 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
50 | redistribute, and sell modified and unmodified copies of the Font
51 | Software, subject to the following conditions:
52 |
53 | 1) Neither the Font Software nor any of its individual components,
54 | in Original or Modified Versions, may be sold by itself.
55 |
56 | 2) Original or Modified Versions of the Font Software may be bundled,
57 | redistributed and/or sold with any software, provided that each copy
58 | contains the above copyright notice and this license. These can be
59 | included either as stand-alone text files, human-readable headers or
60 | in the appropriate machine-readable metadata fields within text or
61 | binary files as long as those fields can be easily viewed by the user.
62 |
63 | 3) No Modified Version of the Font Software may use the Reserved Font
64 | Name(s) unless explicit written permission is granted by the corresponding
65 | Copyright Holder. This restriction only applies to the primary font name as
66 | presented to the users.
67 |
68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69 | Software shall not be used to promote, endorse or advertise any
70 | Modified Version, except to acknowledge the contribution(s) of the
71 | Copyright Holder(s) and the Author(s) or with their explicit written
72 | permission.
73 |
74 | 5) The Font Software, modified or unmodified, in part or in whole,
75 | must be distributed entirely under this license, and must not be
76 | distributed under any other license. The requirement for fonts to
77 | remain under this license does not apply to any document created
78 | using the Font Software.
79 |
80 | TERMINATION
81 | This license becomes null and void if any of the above conditions are
82 | not met.
83 |
84 | DISCLAIMER
85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93 | OTHER DEALINGS IN THE FONT SOFTWARE.
94 |
--------------------------------------------------------------------------------
/docs/css/general.css:
--------------------------------------------------------------------------------
1 | /* Base styles and content styles */
2 |
3 | @import 'variables.css';
4 |
5 | :root {
6 | /* Browser default font-size is 16px, this way 1 rem = 10px */
7 | font-size: 62.5%;
8 | }
9 |
10 | html {
11 | font-family: "Open Sans", sans-serif;
12 | color: var(--fg);
13 | background-color: var(--bg);
14 | text-size-adjust: none;
15 | -webkit-text-size-adjust: none;
16 | }
17 |
18 | body {
19 | margin: 0;
20 | font-size: 1.6rem;
21 | overflow-x: hidden;
22 | }
23 |
24 | code {
25 | font-family: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace !important;
26 | font-size: 0.875em; /* please adjust the ace font size accordingly in editor.js */
27 | }
28 |
29 | /* make long words/inline code not x overflow */
30 | main {
31 | overflow-wrap: break-word;
32 | }
33 |
34 | /* make wide tables scroll if they overflow */
35 | .table-wrapper {
36 | overflow-x: auto;
37 | }
38 |
39 | /* Don't change font size in headers. */
40 | h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
41 | font-size: unset;
42 | }
43 |
44 | .left { float: left; }
45 | .right { float: right; }
46 | .boring { opacity: 0.6; }
47 | .hide-boring .boring { display: none; }
48 | .hidden { display: none !important; }
49 |
50 | h2, h3 { margin-top: 2.5em; }
51 | h4, h5 { margin-top: 2em; }
52 |
53 | .header + .header h3,
54 | .header + .header h4,
55 | .header + .header h5 {
56 | margin-top: 1em;
57 | }
58 |
59 | h1:target::before,
60 | h2:target::before,
61 | h3:target::before,
62 | h4:target::before,
63 | h5:target::before,
64 | h6:target::before {
65 | display: inline-block;
66 | content: "»";
67 | margin-left: -30px;
68 | width: 30px;
69 | }
70 |
71 | /* This is broken on Safari as of version 14, but is fixed
72 | in Safari Technology Preview 117 which I think will be Safari 14.2.
73 | https://bugs.webkit.org/show_bug.cgi?id=218076
74 | */
75 | :target {
76 | scroll-margin-top: calc(var(--menu-bar-height) + 0.5em);
77 | }
78 |
79 | .page {
80 | outline: 0;
81 | padding: 0 var(--page-padding);
82 | margin-top: calc(0px - var(--menu-bar-height)); /* Compensate for the #menu-bar-hover-placeholder */
83 | }
84 | .page-wrapper {
85 | box-sizing: border-box;
86 | }
87 | .js:not(.sidebar-resizing) .page-wrapper {
88 | transition: margin-left 0.3s ease, transform 0.3s ease; /* Animation: slide away */
89 | }
90 |
91 | .content {
92 | overflow-y: auto;
93 | padding: 0 5px 50px 5px;
94 | }
95 | .content main {
96 | margin-left: auto;
97 | margin-right: auto;
98 | max-width: var(--content-max-width);
99 | }
100 | .content p { line-height: 1.45em; }
101 | .content ol { line-height: 1.45em; }
102 | .content ul { line-height: 1.45em; }
103 | .content a { text-decoration: none; }
104 | .content a:hover { text-decoration: underline; }
105 | .content img, .content video { max-width: 100%; }
106 | .content .header:link,
107 | .content .header:visited {
108 | color: var(--fg);
109 | }
110 | .content .header:link,
111 | .content .header:visited:hover {
112 | text-decoration: none;
113 | }
114 |
115 | table {
116 | margin: 0 auto;
117 | border-collapse: collapse;
118 | }
119 | table td {
120 | padding: 3px 20px;
121 | border: 1px var(--table-border-color) solid;
122 | }
123 | table thead {
124 | background: var(--table-header-bg);
125 | }
126 | table thead td {
127 | font-weight: 700;
128 | border: none;
129 | }
130 | table thead th {
131 | padding: 3px 20px;
132 | }
133 | table thead tr {
134 | border: 1px var(--table-header-bg) solid;
135 | }
136 | /* Alternate background colors for rows */
137 | table tbody tr:nth-child(2n) {
138 | background: var(--table-alternate-bg);
139 | }
140 |
141 |
142 | blockquote {
143 | margin: 20px 0;
144 | padding: 0 20px;
145 | color: var(--fg);
146 | background-color: var(--quote-bg);
147 | border-top: .1em solid var(--quote-border);
148 | border-bottom: .1em solid var(--quote-border);
149 | }
150 |
151 |
152 | :not(.footnote-definition) + .footnote-definition,
153 | .footnote-definition + :not(.footnote-definition) {
154 | margin-top: 2em;
155 | }
156 | .footnote-definition {
157 | font-size: 0.9em;
158 | margin: 0.5em 0;
159 | }
160 | .footnote-definition p {
161 | display: inline;
162 | }
163 |
164 | .tooltiptext {
165 | position: absolute;
166 | visibility: hidden;
167 | color: #fff;
168 | background-color: #333;
169 | transform: translateX(-50%); /* Center by moving tooltip 50% of its width left */
170 | left: -8px; /* Half of the width of the icon */
171 | top: -35px;
172 | font-size: 0.8em;
173 | text-align: center;
174 | border-radius: 6px;
175 | padding: 5px 8px;
176 | margin: 5px;
177 | z-index: 1000;
178 | }
179 | .tooltipped .tooltiptext {
180 | visibility: visible;
181 | }
182 |
183 | .chapter li.part-title {
184 | color: var(--sidebar-fg);
185 | margin: 5px 0px;
186 | font-weight: bold;
187 | }
188 |
189 | .result-no-output {
190 | font-style: italic;
191 | }
192 |
--------------------------------------------------------------------------------
/src/interceptor/client.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "io"
6 | "log"
7 | "time"
8 |
9 | "google.golang.org/grpc"
10 |
11 | pb "github.com/jergoo/go-grpc-tutorial/protos/ping" // 引入编译生成的包
12 | )
13 |
14 | // Ping 单次请求-响应模式
15 | func Ping() {
16 | // 以option方式添加拦截器
17 | conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure(), grpc.WithUnaryInterceptor(clientUnaryInterceptor))
18 | if err != nil {
19 | log.Fatal(err)
20 | }
21 | defer conn.Close()
22 |
23 | // 实例化客户端并调用
24 | client := pb.NewPingPongClient(conn)
25 | res, err := client.Ping(context.Background(), &pb.PingRequest{Value: "ping"})
26 | if err != nil {
27 | log.Fatal(err)
28 | }
29 | log.Println(res.Value)
30 | }
31 |
32 | // MultiPong 服务端流模式
33 | func MultiPong() {
34 | conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure())
35 | if err != nil {
36 | log.Fatal(err)
37 | }
38 | defer conn.Close()
39 |
40 | // 实例化客户端并调用
41 | client := pb.NewPingPongClient(conn)
42 | stream, err := client.MultiPong(context.Background(), &pb.PingRequest{Value: "ping"})
43 | if err != nil {
44 | log.Fatal(err)
45 | }
46 |
47 | // 循环接收数据流
48 | for {
49 | msg, err := stream.Recv()
50 | if err != nil {
51 | if err == io.EOF {
52 | break
53 | }
54 | log.Fatal(err)
55 | }
56 | log.Println(msg.Value)
57 | }
58 | }
59 |
60 | // MultiPing 客户端流模式
61 | func MultiPing() {
62 | conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure())
63 | if err != nil {
64 | log.Fatal(err)
65 | }
66 | defer conn.Close()
67 |
68 | // 实例化客户端并调用
69 | client := pb.NewPingPongClient(conn)
70 | stream, err := client.MultiPing(context.Background())
71 | if err != nil {
72 | log.Fatal(err)
73 | }
74 |
75 | // 发送数据
76 | for i := 0; i < 6; i++ {
77 | data := &pb.PingRequest{Value: "ping"}
78 | err = stream.Send(data)
79 | if err != nil {
80 | log.Fatal(err)
81 | }
82 | }
83 |
84 | // 发送结束并获取服务端响应
85 | res, err := stream.CloseAndRecv()
86 | if err != nil {
87 | log.Fatal(err)
88 | }
89 |
90 | log.Println(res.Value)
91 | }
92 |
93 | // MultiPingPong 双向流模式
94 | func MultiPingPong() {
95 | opts := []grpc.DialOption{
96 | grpc.WithInsecure(),
97 | grpc.WithStreamInterceptor(clientStreamInterceptor),
98 | }
99 | conn, err := grpc.Dial("localhost:1234", opts...)
100 | if err != nil {
101 | log.Fatal(err)
102 | }
103 | defer conn.Close()
104 |
105 | // 实例化客户端并调用
106 | client := pb.NewPingPongClient(conn)
107 | stream, err := client.MultiPingPong(context.Background())
108 | if err != nil {
109 | log.Fatal(err)
110 | }
111 |
112 | // 在另一个goroutine中接收数据
113 | c := make(chan struct{})
114 | go func(stream pb.PingPong_MultiPingPongClient, c chan struct{}) {
115 | for {
116 | msg, err := stream.Recv()
117 | if err != nil {
118 | if err == io.EOF {
119 | break
120 | }
121 | log.Fatal(err)
122 | }
123 | log.Printf("recv:%s\n", msg.Value)
124 | }
125 | c <- struct{}{}
126 | }(stream, c)
127 |
128 | // 发送数据
129 | for i := 0; i < 2; i++ {
130 | data := &pb.PingRequest{Value: "ping"}
131 | err = stream.Send(data)
132 | if err != nil {
133 | log.Fatal(err)
134 | }
135 | log.Printf("send:%s\n", data.Value)
136 | time.Sleep(500 * time.Millisecond)
137 | }
138 |
139 | // 结束发送
140 | stream.CloseSend()
141 | // 等待接收完成
142 | <-c
143 | }
144 |
145 | // type UnaryClientInterceptor func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error
146 |
147 | // 客户端拦截器 - 记录请求和响应日志
148 | func clientUnaryInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
149 | // 前置逻辑
150 | log.Printf("[Client Interceptor] send request: %s", method)
151 |
152 | // 发起请求
153 | err := invoker(ctx, method, req, reply, cc, opts...)
154 |
155 | // 后置逻辑
156 | log.Printf("[Client Interceptor] response: %s", reply)
157 |
158 | return err
159 | }
160 |
161 | // type StreamClientInterceptor func(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, streamer Streamer, opts ...CallOption) (ClientStream, error)
162 |
163 | // 客户端拦截器 - 记录stream请求和响应日志
164 | func clientStreamInterceptor(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
165 | // 前置逻辑
166 | log.Printf("[Client Stream Interceptor] send request: %s", method)
167 |
168 | // 请求
169 | s, err := streamer(ctx, desc, cc, method, opts...)
170 | if err != nil {
171 | return nil, err
172 | }
173 |
174 | // 自定义类型包装 ClientStream
175 | return &customClientStream{s}, nil
176 | }
177 |
178 | type customClientStream struct {
179 | grpc.ClientStream
180 | }
181 |
182 | func (s *customClientStream) SendMsg(m interface{}) error {
183 | log.Printf("[Client Stream Interceptor] send: %T", m)
184 | return s.ClientStream.SendMsg(m)
185 | }
186 |
187 | func (s *customClientStream) RecvMsg(m interface{}) error {
188 | time.Sleep(1000 * time.Millisecond)
189 | log.Printf("[Client Stream Interceptor] recv: %T", m)
190 | return s.ClientStream.RecvMsg(m)
191 | }
192 |
--------------------------------------------------------------------------------
/src/protos/google/protobuf/duration.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | syntax = "proto3";
32 |
33 | package google.protobuf;
34 |
35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
36 | option cc_enable_arenas = true;
37 | option go_package = "google.golang.org/protobuf/types/known/durationpb";
38 | option java_package = "com.google.protobuf";
39 | option java_outer_classname = "DurationProto";
40 | option java_multiple_files = true;
41 | option objc_class_prefix = "GPB";
42 |
43 | // A Duration represents a signed, fixed-length span of time represented
44 | // as a count of seconds and fractions of seconds at nanosecond
45 | // resolution. It is independent of any calendar and concepts like "day"
46 | // or "month". It is related to Timestamp in that the difference between
47 | // two Timestamp values is a Duration and it can be added or subtracted
48 | // from a Timestamp. Range is approximately +-10,000 years.
49 | //
50 | // # Examples
51 | //
52 | // Example 1: Compute Duration from two Timestamps in pseudo code.
53 | //
54 | // Timestamp start = ...;
55 | // Timestamp end = ...;
56 | // Duration duration = ...;
57 | //
58 | // duration.seconds = end.seconds - start.seconds;
59 | // duration.nanos = end.nanos - start.nanos;
60 | //
61 | // if (duration.seconds < 0 && duration.nanos > 0) {
62 | // duration.seconds += 1;
63 | // duration.nanos -= 1000000000;
64 | // } else if (duration.seconds > 0 && duration.nanos < 0) {
65 | // duration.seconds -= 1;
66 | // duration.nanos += 1000000000;
67 | // }
68 | //
69 | // Example 2: Compute Timestamp from Timestamp + Duration in pseudo code.
70 | //
71 | // Timestamp start = ...;
72 | // Duration duration = ...;
73 | // Timestamp end = ...;
74 | //
75 | // end.seconds = start.seconds + duration.seconds;
76 | // end.nanos = start.nanos + duration.nanos;
77 | //
78 | // if (end.nanos < 0) {
79 | // end.seconds -= 1;
80 | // end.nanos += 1000000000;
81 | // } else if (end.nanos >= 1000000000) {
82 | // end.seconds += 1;
83 | // end.nanos -= 1000000000;
84 | // }
85 | //
86 | // Example 3: Compute Duration from datetime.timedelta in Python.
87 | //
88 | // td = datetime.timedelta(days=3, minutes=10)
89 | // duration = Duration()
90 | // duration.FromTimedelta(td)
91 | //
92 | // # JSON Mapping
93 | //
94 | // In JSON format, the Duration type is encoded as a string rather than an
95 | // object, where the string ends in the suffix "s" (indicating seconds) and
96 | // is preceded by the number of seconds, with nanoseconds expressed as
97 | // fractional seconds. For example, 3 seconds with 0 nanoseconds should be
98 | // encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should
99 | // be expressed in JSON format as "3.000000001s", and 3 seconds and 1
100 | // microsecond should be expressed in JSON format as "3.000001s".
101 | //
102 | //
103 | message Duration {
104 | // Signed seconds of the span of time. Must be from -315,576,000,000
105 | // to +315,576,000,000 inclusive. Note: these bounds are computed from:
106 | // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years
107 | int64 seconds = 1;
108 |
109 | // Signed fractions of a second at nanosecond resolution of the span
110 | // of time. Durations less than one second are represented with a 0
111 | // `seconds` field and a positive or negative `nanos` field. For durations
112 | // of one second or more, a non-zero value for the `nanos` field must be
113 | // of the same sign as the `seconds` field. Must be from -999,999,999
114 | // to +999,999,999 inclusive.
115 | int32 nanos = 2;
116 | }
117 |
--------------------------------------------------------------------------------
/src/docs/advance/auth.md:
--------------------------------------------------------------------------------
1 | # 安全认证
2 |
3 | ---
4 |
5 | 服务开发中需要考虑服务的安全性,如连接是否加密,用户请求是否有权限等,gRPC 支持基于 TLS 的认证保证连接的安全性,通知也支持基于 token 的认证方式,用于对用户做权限认证。
6 |
7 | **源码目录:**
8 |
9 | ```
10 | |—- src/
11 | |-- auth/
12 | |-- keys/ // 证书目录
13 | |—— client.go // 客户端
14 | |—— server.go // 服务端
15 | |—- protos/ping/
16 | |—— ping.proto // protobuf描述文件
17 | |—— ping.pb.go // protoc编译生成
18 | |-- ping_grpc.pb.go // protoc编译生成
19 | ```
20 |
21 |
22 | ## TLS认证
23 |
24 | 首先需要准备服务端证书,在 keys 目录存放证书文件。
25 |
26 | ```sh
27 | $ cd src/auth/keys
28 | # 生成证书,-config 替换为对应系统的openssl配置文件目录
29 | $ openssl req -newkey rsa:2048 -x509 -nodes -sha256 -days 3650 \
30 | -keyout server.key -new -out server.crt \
31 | -subj /CN=grpc.server -reqexts SAN -extensions SAN \
32 | -config <(cat /System/Library/OpenSSL/openssl.cnf \
33 | <(printf '[SAN]\nsubjectAltName=DNS:grpc.server'))
34 | ```
35 |
36 | 生成证书文件后,我们就可以在服务端通过 `ServerOption` 开启 TLS 了,示例如下:
37 |
38 | ```go
39 | // src/auth/server.go
40 |
41 | func main() {
42 | creds, err := credentials.NewServerTLSFromFile("keys/server.crt", "keys/server.key")
43 | if err != nil {
44 | log.Fatalf("load crt fail:%v", err)
45 | }
46 | opts := []grpc.ServerOption{
47 | grpc.Creds(creds),
48 | }
49 |
50 | srv := grpc.NewServer(opts...)
51 |
52 | ...
53 | ```
54 |
55 | 类似的,在客户端使用 `DialOption` 开启 TLS,示例如下:
56 |
57 | ```golang
58 | func Ping() {
59 | // 读取服务端证书,并制定对应服务名
60 | cred, err := credentials.NewClientTLSFromFile("keys/server.crt", "go-grpc-tutorial")
61 | if err != nil {
62 | log.Fatalf("load crt fail: %v", err)
63 | }
64 |
65 | // 连接配置
66 | opts := []grpc.DialOption{
67 | grpc.WithTransportCredentials(cred),
68 | }
69 | conn, err := grpc.Dial("localhost:1234", opts...)
70 | if err != nil {
71 | log.Fatal(err)
72 | }
73 |
74 | ...
75 | ```
76 |
77 | ## Token 认证
78 |
79 | TLS 认证是针对连接的安全加密方式,实际应用中还需要针对每个用户请求进行认证,常用的方式就是基于 token 认证,gRPC 使用 `grpc.PerRPCCredentials` 接口对此提供了支持。
80 |
81 | ```go
82 | // PerRPCCredentials defines the common interface for the credentials which need to
83 | // attach security information to every RPC (e.g., oauth2).
84 | type PerRPCCredentials interface {
85 | // GetRequestMetadata gets the current request metadata, refreshing
86 | // tokens if required. This should be called by the transport layer on
87 | // each request, and the data should be populated in headers or other
88 | // context. If a status code is returned, it will be used as the status
89 | // for the RPC. uri is the URI of the entry point for the request.
90 | // When supported by the underlying implementation, ctx can be used for
91 | // timeout and cancellation. Additionally, RequestInfo data will be
92 | // available via ctx to this call.
93 | // TODO(zhaoq): Define the set of the qualified keys instead of leaving
94 | // it as an arbitrary string.
95 | GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error)
96 | // RequireTransportSecurity indicates whether the credentials requires
97 | // transport security.
98 | RequireTransportSecurity() bool
99 | }
100 |
101 | ```
102 |
103 | 客户端示例如下:
104 |
105 | ```go
106 | // src/auth/client.go
107 |
108 | func Ping() {
109 | // 增加认证 Dial Option
110 | conn, err := grpc.Dial("localhost:1234", grpc.WithPerRPCCredentials(CustomAuth{Token: "1234567890"}))
111 | if err != nil {
112 | log.Fatal(err)
113 | }
114 | defer conn.Close()
115 |
116 | // 实例化客户端并调用
117 | client := pb.NewPingPongClient(conn)
118 | res, err := client.Ping(context.Background(), &pb.PingRequest{Value: "ping"})
119 | if err != nil {
120 | log.Fatal(err)
121 | }
122 | log.Println(res.Value)
123 | }
124 |
125 | // CustomAuth 自定义认证类型
126 | type CustomAuth struct {
127 | Token string
128 | }
129 |
130 | // GetRequestMetadata 生成认证信息
131 | func (a CustomAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
132 | return map[string]string{
133 | "authorization": a.Token,
134 | }, nil
135 | }
136 |
137 | // RequireTransportSecurity 是否开启 TLS
138 | func (a CustomAuth) RequireTransportSecurity() bool {
139 | return false
140 | }
141 | ```
142 |
143 | 这里定义了一个 `CustomAuth` 类型,并实现了 `grpc.PerRPCCredentials` 接口的两个方法,通过 `grpc.WithPerRPCCredentials` 方法转换为 `DialOption` 类型初始化连接,这样每次 rpc 调用时 token 信息会通过请求的metadata 传输到服务端。
144 |
145 | 既然是通过 metadata 传输 token 信息,那么服务端认证就非常简单了,可以实现一个拦截器统一处理请求中的 token,示例如下:
146 |
147 | ```golang
148 | // src/auth/server.go
149 | ...
150 |
151 | // 服务端拦截器 - token 认证
152 | func authInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
153 | md, ok := metadata.FromIncomingContext(ctx)
154 | if !ok {
155 | return nil, errors.New("authorization missing")
156 | }
157 |
158 | var token string
159 | if auth, ok := md["authorization"]; ok {
160 | token = auth[0]
161 | }
162 | if token != "1234567890" {
163 | return nil, grpc.Errorf(codes.Unauthenticated, "token invalid")
164 | }
165 |
166 | // 处理请求
167 | return handler(ctx, req)
168 | }
169 |
170 | // 启动server
171 | func main() {
172 | srv := grpc.NewServer(grpc.UnaryInterceptor(authInterceptor))
173 |
174 | ...
175 | ```
176 |
177 | 以上就是基于 token 的认证方法,还是比较简单的,实际应用中可以根据自己的业务需求生成不同类型的 token,`google.golang.org/grpc/credentials/oauth`包也对 oauth2 和 jwt 提供了支持,感兴趣可以看一下 `oauth.NewOauthAccess(token *oauth2.Token)` 和 `oauth.NewJWTAccessFromKey(jsonKey []byte)` 方法,实际上也是实现了 `grpc.PerRPCCredentials` 接口。
178 |
179 | ---
180 |
--------------------------------------------------------------------------------
/archive/src/proto/hello/hello.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go. DO NOT EDIT.
2 | // source: hello/hello.proto
3 |
4 | /*
5 | Package hello is a generated protocol buffer package.
6 |
7 | It is generated from these files:
8 | hello/hello.proto
9 |
10 | It has these top-level messages:
11 | HelloRequest
12 | HelloResponse
13 | */
14 | package hello
15 |
16 | import proto "github.com/golang/protobuf/proto"
17 | import fmt "fmt"
18 | import math "math"
19 |
20 | import (
21 | context "golang.org/x/net/context"
22 | grpc "google.golang.org/grpc"
23 | )
24 |
25 | // Reference imports to suppress errors if they are not otherwise used.
26 | var _ = proto.Marshal
27 | var _ = fmt.Errorf
28 | var _ = math.Inf
29 |
30 | // This is a compile-time assertion to ensure that this generated file
31 | // is compatible with the proto package it is being compiled against.
32 | // A compilation error at this line likely means your copy of the
33 | // proto package needs to be updated.
34 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
35 |
36 | // HelloRequest 请求结构
37 | type HelloRequest struct {
38 | Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
39 | }
40 |
41 | func (m *HelloRequest) Reset() { *m = HelloRequest{} }
42 | func (m *HelloRequest) String() string { return proto.CompactTextString(m) }
43 | func (*HelloRequest) ProtoMessage() {}
44 | func (*HelloRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
45 |
46 | func (m *HelloRequest) GetName() string {
47 | if m != nil {
48 | return m.Name
49 | }
50 | return ""
51 | }
52 |
53 | // HelloResponse 响应结构
54 | type HelloResponse struct {
55 | Message string `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"`
56 | }
57 |
58 | func (m *HelloResponse) Reset() { *m = HelloResponse{} }
59 | func (m *HelloResponse) String() string { return proto.CompactTextString(m) }
60 | func (*HelloResponse) ProtoMessage() {}
61 | func (*HelloResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
62 |
63 | func (m *HelloResponse) GetMessage() string {
64 | if m != nil {
65 | return m.Message
66 | }
67 | return ""
68 | }
69 |
70 | func init() {
71 | proto.RegisterType((*HelloRequest)(nil), "hello.HelloRequest")
72 | proto.RegisterType((*HelloResponse)(nil), "hello.HelloResponse")
73 | }
74 |
75 | // Reference imports to suppress errors if they are not otherwise used.
76 | var _ context.Context
77 | var _ grpc.ClientConn
78 |
79 | // This is a compile-time assertion to ensure that this generated file
80 | // is compatible with the grpc package it is being compiled against.
81 | const _ = grpc.SupportPackageIsVersion4
82 |
83 | // Client API for Hello service
84 |
85 | type HelloClient interface {
86 | // 定义SayHello方法
87 | SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error)
88 | }
89 |
90 | type helloClient struct {
91 | cc *grpc.ClientConn
92 | }
93 |
94 | func NewHelloClient(cc *grpc.ClientConn) HelloClient {
95 | return &helloClient{cc}
96 | }
97 |
98 | func (c *helloClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error) {
99 | out := new(HelloResponse)
100 | err := grpc.Invoke(ctx, "/hello.Hello/SayHello", in, out, c.cc, opts...)
101 | if err != nil {
102 | return nil, err
103 | }
104 | return out, nil
105 | }
106 |
107 | // Server API for Hello service
108 |
109 | type HelloServer interface {
110 | // 定义SayHello方法
111 | SayHello(context.Context, *HelloRequest) (*HelloResponse, error)
112 | }
113 |
114 | func RegisterHelloServer(s *grpc.Server, srv HelloServer) {
115 | s.RegisterService(&_Hello_serviceDesc, srv)
116 | }
117 |
118 | func _Hello_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
119 | in := new(HelloRequest)
120 | if err := dec(in); err != nil {
121 | return nil, err
122 | }
123 | if interceptor == nil {
124 | return srv.(HelloServer).SayHello(ctx, in)
125 | }
126 | info := &grpc.UnaryServerInfo{
127 | Server: srv,
128 | FullMethod: "/hello.Hello/SayHello",
129 | }
130 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
131 | return srv.(HelloServer).SayHello(ctx, req.(*HelloRequest))
132 | }
133 | return interceptor(ctx, in, info, handler)
134 | }
135 |
136 | var _Hello_serviceDesc = grpc.ServiceDesc{
137 | ServiceName: "hello.Hello",
138 | HandlerType: (*HelloServer)(nil),
139 | Methods: []grpc.MethodDesc{
140 | {
141 | MethodName: "SayHello",
142 | Handler: _Hello_SayHello_Handler,
143 | },
144 | },
145 | Streams: []grpc.StreamDesc{},
146 | Metadata: "hello/hello.proto",
147 | }
148 |
149 | func init() { proto.RegisterFile("hello/hello.proto", fileDescriptor0) }
150 |
151 | var fileDescriptor0 = []byte{
152 | // 139 bytes of a gzipped FileDescriptorProto
153 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xcc, 0x48, 0xcd, 0xc9,
154 | 0xc9, 0xd7, 0x07, 0x93, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0xac, 0x60, 0x8e, 0x92, 0x12,
155 | 0x17, 0x8f, 0x07, 0x88, 0x11, 0x94, 0x5a, 0x58, 0x9a, 0x5a, 0x5c, 0x22, 0x24, 0xc4, 0xc5, 0x92,
156 | 0x97, 0x98, 0x9b, 0x2a, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x19, 0x04, 0x66, 0x2b, 0x69, 0x72, 0xf1,
157 | 0x42, 0xd5, 0x14, 0x17, 0xe4, 0xe7, 0x15, 0xa7, 0x0a, 0x49, 0x70, 0xb1, 0xe7, 0xa6, 0x16, 0x17,
158 | 0x27, 0xa6, 0xc3, 0xd4, 0xc1, 0xb8, 0x46, 0x0e, 0x5c, 0xac, 0x60, 0xa5, 0x42, 0xe6, 0x5c, 0x1c,
159 | 0xc1, 0x89, 0x95, 0x10, 0xb6, 0xb0, 0x1e, 0xc4, 0x62, 0x64, 0x8b, 0xa4, 0x44, 0x50, 0x05, 0x21,
160 | 0x26, 0x2b, 0x31, 0x38, 0xb1, 0x47, 0x41, 0x5c, 0x96, 0xc4, 0x06, 0x76, 0xa7, 0x31, 0x20, 0x00,
161 | 0x00, 0xff, 0xff, 0xc5, 0xbb, 0x84, 0x66, 0xbc, 0x00, 0x00, 0x00,
162 | }
163 |
--------------------------------------------------------------------------------
/archive/docs/c2/interceptor.md:
--------------------------------------------------------------------------------
1 | # Interceptor 拦截器
2 |
3 | grpc服务端和客户端都提供了interceptor功能,功能类似middleware,很适合在这里处理验证、日志等流程。
4 |
5 | 在自定义Token认证的示例中,认证信息是由每个服务中的方法处理并认证的,如果有大量的接口方法,这种姿势就太不优雅了,每个接口实现都要先处理认证信息。这个时候interceptor就可以用来解决了这个问题,在请求被转到具体接口之前处理认证信息,一处认证,到处无忧。
6 | 在客户端,我们增加一个请求日志,记录请求相关的参数和耗时等等。修改hello_token项目实现:
7 |
8 |
9 | ## 目录结构
10 |
11 | ```
12 | |—— hello_interceptor/
13 | |—— client/
14 | |—— main.go // 客户端
15 | |—— server/
16 | |—— main.go // 服务端
17 | |—— keys/ // 证书目录
18 | |—— server.key
19 | |—— server.pem
20 | |—— proto/
21 | |—— hello/
22 | |—— hello.proto // proto描述文件
23 | |—— hello.pb.go // proto编译后文件
24 | ```
25 |
26 | ## 示例代码
27 |
28 | **Step 1. 服务端interceptor:**
29 | > hello_interceptor/server/main.go
30 |
31 | ```go
32 | package main
33 |
34 | import (
35 | "fmt"
36 | "net"
37 |
38 | pb "github.com/jergoo/go-grpc-example/proto/hello"
39 |
40 | "golang.org/x/net/context"
41 | "google.golang.org/grpc"
42 | "google.golang.org/grpc/codes" // grpc 响应状态码
43 | "google.golang.org/grpc/credentials" // grpc认证包
44 | "google.golang.org/grpc/grpclog"
45 | "google.golang.org/grpc/metadata" // grpc metadata包
46 | )
47 |
48 | const (
49 | // Address gRPC服务地址
50 | Address = "127.0.0.1:50052"
51 | )
52 |
53 | // 定义helloService并实现约定的接口
54 | type helloService struct{}
55 |
56 | // HelloService Hello服务
57 | var HelloService = helloService{}
58 |
59 | // SayHello 实现Hello服务接口
60 | func (h helloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
61 | resp := new(pb.HelloResponse)
62 | resp.Message = fmt.Sprintf("Hello %s.", in.Name)
63 |
64 | return resp, nil
65 | }
66 |
67 | func main() {
68 | listen, err := net.Listen("tcp", Address)
69 | if err != nil {
70 | grpclog.Fatalf("Failed to listen: %v", err)
71 | }
72 |
73 | var opts []grpc.ServerOption
74 |
75 | // TLS认证
76 | creds, err := credentials.NewServerTLSFromFile("../../keys/server.pem", "../../keys/server.key")
77 | if err != nil {
78 | grpclog.Fatalf("Failed to generate credentials %v", err)
79 | }
80 |
81 | opts = append(opts, grpc.Creds(creds))
82 |
83 | // 注册interceptor
84 | opts = append(opts, grpc.UnaryInterceptor(interceptor))
85 |
86 | // 实例化grpc Server
87 | s := grpc.NewServer(opts...)
88 |
89 | // 注册HelloService
90 | pb.RegisterHelloServer(s, HelloService)
91 |
92 | grpclog.Println("Listen on " + Address + " with TLS + Token + Interceptor")
93 |
94 | s.Serve(listen)
95 | }
96 |
97 | // auth 验证Token
98 | func auth(ctx context.Context) error {
99 | md, ok := metadata.FromContext(ctx)
100 | if !ok {
101 | return grpc.Errorf(codes.Unauthenticated, "无Token认证信息")
102 | }
103 |
104 | var (
105 | appid string
106 | appkey string
107 | )
108 |
109 | if val, ok := md["appid"]; ok {
110 | appid = val[0]
111 | }
112 |
113 | if val, ok := md["appkey"]; ok {
114 | appkey = val[0]
115 | }
116 |
117 | if appid != "101010" || appkey != "i am key" {
118 | return grpc.Errorf(codes.Unauthenticated, "Token认证信息无效: appid=%s, appkey=%s", appid, appkey)
119 | }
120 |
121 | return nil
122 | }
123 |
124 | // interceptor 拦截器
125 | func interceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
126 | err := auth(ctx)
127 | if err != nil {
128 | return nil, err
129 | }
130 | // 继续处理请求
131 | return handler(ctx, req)
132 | }
133 | ```
134 |
135 | **Step 2. 实现客户端interceptor:**
136 |
137 | > hello_intercepror/client/main.go
138 |
139 | ```golang
140 | package main
141 |
142 | import (
143 | "time"
144 |
145 | pb "github.com/jergoo/go-grpc-example/proto/hello" // 引入proto包
146 |
147 | "golang.org/x/net/context"
148 | "google.golang.org/grpc"
149 | "google.golang.org/grpc/credentials" // 引入grpc认证包
150 | "google.golang.org/grpc/grpclog"
151 | )
152 |
153 | const (
154 | // Address gRPC服务地址
155 | Address = "127.0.0.1:50052"
156 |
157 | // OpenTLS 是否开启TLS认证
158 | OpenTLS = true
159 | )
160 |
161 | // customCredential 自定义认证
162 | type customCredential struct{}
163 |
164 | // GetRequestMetadata 实现自定义认证接口
165 | func (c customCredential) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
166 | return map[string]string{
167 | "appid": "101010",
168 | "appkey": "i am key",
169 | }, nil
170 | }
171 |
172 | // RequireTransportSecurity 自定义认证是否开启TLS
173 | func (c customCredential) RequireTransportSecurity() bool {
174 | return OpenTLS
175 | }
176 |
177 | func main() {
178 | var err error
179 | var opts []grpc.DialOption
180 |
181 | if OpenTLS {
182 | // TLS连接
183 | creds, err := credentials.NewClientTLSFromFile("../../keys/server.pem", "server name")
184 | if err != nil {
185 | grpclog.Fatalf("Failed to create TLS credentials %v", err)
186 | }
187 | opts = append(opts, grpc.WithTransportCredentials(creds))
188 | } else {
189 | opts = append(opts, grpc.WithInsecure())
190 | }
191 |
192 | // 指定自定义认证
193 | opts = append(opts, grpc.WithPerRPCCredentials(new(customCredential)))
194 | // 指定客户端interceptor
195 | opts = append(opts, grpc.WithUnaryInterceptor(interceptor))
196 |
197 | conn, err := grpc.Dial(Address, opts...)
198 | if err != nil {
199 | grpclog.Fatalln(err)
200 | }
201 | defer conn.Close()
202 |
203 | // 初始化客户端
204 | c := pb.NewHelloClient(conn)
205 |
206 | // 调用方法
207 | req := &pb.HelloRequest{Name: "gRPC"}
208 | res, err := c.SayHello(context.Background(), req)
209 | if err != nil {
210 | grpclog.Fatalln(err)
211 | }
212 |
213 | grpclog.Println(res.Message)
214 | }
215 |
216 | // interceptor 客户端拦截器
217 | func interceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
218 | start := time.Now()
219 | err := invoker(ctx, method, req, reply, cc, opts...)
220 | grpclog.Printf("method=%s req=%v rep=%v duration=%s error=%v\n", method, req, reply, time.Since(start), err)
221 | return err
222 | }
223 |
224 | ```
225 |
226 | ## 运行结果
227 |
228 | ```sh
229 | $ cd hello_inteceptor/server && go run main.go
230 | Listen on 127.0.0.1:50052 with TLS + Token + Interceptor
231 | ```
232 |
233 | ```sh
234 | $ cd hello_inteceptor/client && go run main.go
235 | method=/hello.Hello/SayHello req=name:"gRPC" rep=message:"Hello gRPC." duration=33.879699ms error=
236 |
237 | Hello gRPC.
238 | ```
239 |
240 |
241 | **项目推荐:** [go-grpc-middleware](https://github.com/grpc-ecosystem/go-grpc-middleware)
242 |
243 | 这个项目对interceptor进行了封装,支持多个拦截器的链式组装,对于需要多种处理的地方使用起来会更方便些。
244 |
245 |
--------------------------------------------------------------------------------
/src/docs/basic/stream.md:
--------------------------------------------------------------------------------
1 | # gRPC 流
2 |
3 | ---
4 |
5 | 从其名称可以理解,流就是持续不断的传输。有一些业务场景请求或者响应的数据量比较大,不适合使用普通的 RPC 调用通过一次请求-响应处理,一方面是考虑数据量大对请求响应时间的影响,另一方面业务场景的设计不一定需要一次性处理完所有数据,这时就可以使用流来分批次传输数据。gRPC支持单向流和双向流,只需要在 service 的 rpc 方法描述中通过 `stream` 关键字指定启用流特性就好了,下面通过两个示例来说明使用方法。
6 |
7 | **源码目录:**
8 |
9 | ```
10 | |—- src/
11 | |-- ping/
12 | |—— client.go // 客户端
13 | |—— server.go // 服务端
14 | |—- protos/ping/
15 | |—— ping.proto // protobuf描述文件
16 | |—— ping.pb.go // protoc编译生成
17 | |-- ping_grpc.pb.go // protoc编译生成
18 | ```
19 |
20 | ## 单向流
21 |
22 | 单向流是指客户端和服务端只有一端开启流特性,这里的单向特指**发送数据的方向**。
23 |
24 | * 当服务端开启流时,客户端和普通 RPC 调用一样通过一次请求发送数据,服务端通过流分批次响应。
25 | * 当客户端开启流时,客户端通过流分批次发送请求数据,服务端接完所有数据后统一响应一次。
26 |
27 | ### 服务端流
28 |
29 | 定义一个 `MultiPong` 方法,在服务端开启流,功能是接收到客户端的请求后响应10次 pong 消息。
30 |
31 | ```protobuf
32 | ...
33 |
34 | service PingPong {
35 | // 服务端流模式,在响应消息前添加 stream 关键字
36 | rpc MultiPong(PingRequest) returns (stream PongResponse);
37 | }
38 |
39 | ...
40 | ```
41 |
42 | **服务端实现**:第二个参数为 stream 对象的引用,可以通过它的 `Send` 方法发送数据。
43 |
44 | ```go
45 | // src/ping/server.go
46 |
47 | // MultiPong 服务端流模式
48 | func (s *PingPongServer) MultiPong(req *pb.PingRequest, stream pb.PingPong_MultiPongServer) error {
49 | for i := 0; i < 10; i++ {
50 | data := &pb.PongResponse{Value: "pong"}
51 | // 发送消息
52 | err := stream.Send(data)
53 | if err != nil {
54 | return err
55 | }
56 | }
57 | return nil
58 | }
59 | ```
60 |
61 | **客户端实现**:请求方式和普通 RPC 没有区别,重点关注对响应数据流的处理,通过一个 for 循环接收数据直到结束。
62 |
63 | ```go
64 | // src/ping/client.go
65 |
66 | func MultiPong() {
67 |
68 | ...
69 |
70 | // 获得对 stream 对象的引用
71 | stream, err := client.MultiPong(context.Background(), &pb.PingRequest{Value: "ping"})
72 | if err != nil {
73 | log.Fatal(err)
74 | }
75 |
76 | // 循环接收响应数据流
77 | for {
78 | msg, err := stream.Recv()
79 | if err != nil {
80 | // 数据结束
81 | if err == io.EOF {
82 | break
83 | }
84 | log.Fatal(err)
85 | }
86 | log.Println(msg.Value)
87 | }
88 | }
89 | ```
90 |
91 | ### 客户端流
92 |
93 | 定义一个 `MultiPing` 方法,在客户端开启流,功能是持续发送多个 ping 请求,服务端统一响应一次。
94 |
95 | ```protobuf
96 | ...
97 |
98 | service PingPong {
99 | // 客户端流模式,在请求消息前添加 stream 关键字
100 | rpc MultiPing(stream PingRequest) returns (PongResponse);
101 | }
102 |
103 | ...
104 | ```
105 |
106 | **服务端实现**:只有一个参数为 stream 对象的引用,可以通过它的 `Recv` 方法接收数据。使用 `SendAndClose` 方法关闭流并响应,服务端可以根据需要提前关闭。
107 |
108 | ```go
109 | // src/ping/server.go
110 |
111 | // MultiPing 客户端流模式
112 | func (s *PingPongServer) MultiPing(stream pb.PingPong_MultiPingServer) error {
113 | msgs := []string{}
114 | for {
115 | // 提前结束接收消息
116 | if len(msgs) > 5 {
117 | return stream.SendAndClose(&pb.PongResponse{Value: "ping enough, max 5"})
118 | }
119 |
120 | msg, err := stream.Recv()
121 | if err != nil {
122 | // 客户端消息结束,返回响应信息
123 | if err == io.EOF {
124 | return stream.SendAndClose(&pb.PongResponse{Value: fmt.Sprintf("got %d ping", len(msgs))})
125 | }
126 | return err
127 | }
128 | msgs = append(msgs, msg.Value)
129 | }
130 | }
131 | ```
132 |
133 | **客户端实现**:调用 `MultiPing` 方法时不再指定请求参数,而是通过返回的 stream 对象的 `Send` 分批发送数据。
134 |
135 | ```go
136 | // src/ping/client.go
137 |
138 | func MultiPing() {
139 | ...
140 |
141 | // 调用并得到 stream 对象
142 | stream, err := client.MultiPing(context.Background())
143 | if err != nil {
144 | log.Fatal(err)
145 | }
146 |
147 | // 发送数据
148 | for i := 0; i < 6; i++ {
149 | data := &pb.PingRequest{Value: "ping"}
150 | err = stream.Send(data)
151 | if err != nil {
152 | log.Fatal(err)
153 | }
154 | }
155 |
156 | // 发送结束并获取服务端响应
157 | res, err := stream.CloseAndRecv()
158 | if err != nil {
159 | log.Fatal(err)
160 | }
161 |
162 | log.Println(res.Value)
163 | }
164 | ```
165 |
166 | > 运行结果:
167 | > ```sh
168 | > // 发送5个ping
169 | > 2022/09/27 00:00:00 got 5 ping
170 | >
171 | > // 发送10个ping
172 | > 2022/09/27 00:00:00 ping enough, max 5
173 | > ```
174 |
175 | ## 双向流
176 |
177 | 双向流是指客户端在发送数据和服务端响应数据的过程中都启用流特性,实际上单向流只是双向流的特例,有了上面的基础,双向流就很好理解了。
178 |
179 | 定义一个 `MultiPingPong` 方法,在客户端和服务端都开启流,功能是服务端每接收到两个 ping 就响应一次 pong。
180 |
181 | ```protobuf
182 | ...
183 |
184 | service PingPong {
185 | // 双向流模式
186 | rpc MultiPingPong(stream PingRequest) returns (stream PongResponse);
187 | }
188 |
189 | ...
190 | ```
191 |
192 | **服务端实现**:同样通过 stream 的 `Recv` 和 `Send` 方法接收和发送数据。
193 |
194 | ```go
195 | // src/ping/server.go
196 |
197 | func (s *PingPongServer) MultiPingPong(stream pb.PingPong_MultiPingPongServer) error {
198 | msgs := []string{}
199 | for {
200 | // 接收消息
201 | msg, err := stream.Recv()
202 | if err != nil {
203 | if err == io.EOF {
204 | break
205 | }
206 | return err
207 | }
208 | msgs = append(msgs, msg.Value)
209 |
210 | // 每收到两个消息响应一次
211 | if len(msgs)%2 == 0 {
212 | err = stream.Send(&pb.PongResponse{Value: "pong"})
213 | if err != nil {
214 | return err
215 | }
216 | }
217 | }
218 | return nil
219 | }
220 | ```
221 |
222 | **客户端实现**:这里在另外一个 goroutine 里处理接收数据的逻辑来演示同时发送和接收数据。
223 |
224 | ```go
225 | // src/ping/client.go
226 | func MultiPingPong() {
227 | ...
228 |
229 | stream, err := client.MultiPingPong(context.Background())
230 | if err != nil {
231 | log.Fatal(err)
232 | }
233 |
234 | // 在另一个goroutine中处理接收数据
235 | c := make(chan struct{})
236 | go func(stream pb.PingPong_MultiPingPongClient, c chan struct{}) {
237 | defer func() {
238 | c <- struct{}{}
239 | }()
240 | for {
241 | msg, err := stream.Recv()
242 | if err != nil {
243 | if err == io.EOF {
244 | break
245 | }
246 | log.Fatal(err)
247 | }
248 | log.Printf("recv:%s\n", msg.Value)
249 | }
250 | }(stream, c)
251 |
252 | // 发送数据
253 | for i := 0; i < 6; i++ {
254 | data := &pb.PingRequest{Value: "ping"}
255 | err = stream.Send(data)
256 | if err != nil {
257 | log.Fatal(err)
258 | }
259 | log.Printf("send:%s\n", data.Value)
260 |
261 | // 延时一段时间发送,等待响应结果
262 | time.Sleep(500 * time.Millisecond)
263 | }
264 |
265 | // 结束发送
266 | stream.CloseSend()
267 | // 等待接收完成
268 | <-c
269 | }
270 |
271 | ```
272 |
273 | > 运行结果:
274 | > ```sh
275 | > 2022/09/29 09:09:59 send:ping
276 | > 2022/09/29 09:10:00 send:ping
277 | > 2022/09/29 09:10:00 recv:pong
278 | > 2022/09/29 09:10:00 send:ping
279 | > 2022/09/29 09:10:01 send:ping
280 | > 2022/09/29 09:10:01 recv:pong
281 | > 2022/09/29 09:10:01 send:ping
282 | > 2022/09/29 09:10:02 send:ping
283 | > 2022/09/29 09:10:02 recv:pong
284 | > ```
285 |
286 | ---
287 |
--------------------------------------------------------------------------------