├── gin
├── a.txt
├── b.txt
├── key.pem
├── go.mod
├── cert.pem
├── README.md
├── gin.go
└── go.sum
├── timer
├── go.mod
├── main.go
└── README.md
├── oauth2
├── go.mod
├── public
│ ├── index.tmpl
│ └── welcome.html
└── main.go
├── os-exec
├── go.mod
├── testcmd
│ ├── testcmd
│ └── main.go
├── main.go
└── README.md
├── recover
├── go.mod
├── recover.go
└── README.md
├── windows_api
├── go.mod
├── go.sum
└── main_windows.go
├── swaggo-gin
├── Makefile
├── api.md
├── doc.go
├── main.go
├── go.mod
├── docs
│ ├── swagger.yaml
│ ├── swagger.json
│ └── docs.go
└── handle.go
├── benchmark
├── main.go
├── go.mod
├── main_test.go
├── go.sum
└── README.md
├── grpc
├── README.md
├── go.mod
├── demo1
│ ├── helloworld
│ │ ├── hello_world.proto
│ │ └── hello_world.pb.go
│ └── README.md
├── demo2
│ ├── helloworld
│ │ ├── hello_world.proto
│ │ └── hello_world.pb.go
│ ├── client
│ │ └── client.go
│ ├── server
│ │ └── server.go
│ └── README.md
├── demo3
│ ├── helloworld
│ │ └── hello_world.proto
│ ├── client
│ │ ├── certs
│ │ │ ├── root.pem
│ │ │ ├── test_client.key
│ │ │ └── test_client.pem
│ │ └── client.go
│ ├── server
│ │ ├── certs
│ │ │ ├── root.pem
│ │ │ ├── test_server.key
│ │ │ └── test_server.pem
│ │ └── server.go
│ └── README.md
└── Makefile
├── .gitignore
├── gomod
├── main.go
├── go.mod
└── README.md
├── json
└── tag
│ └── tag.go
├── restful-api
├── go.mod
├── main.go
├── README.md
└── go.sum
└── README.md
/gin/a.txt:
--------------------------------------------------------------------------------
1 | hello world , I`m file a.txt
--------------------------------------------------------------------------------
/gin/b.txt:
--------------------------------------------------------------------------------
1 | hello world , I`m file b.txt
--------------------------------------------------------------------------------
/timer/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/razeencheng/demo-go/timer
2 |
3 | go 1.18
4 |
--------------------------------------------------------------------------------
/oauth2/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/razeencheng/demo-go/oauth2
2 |
3 | go 1.18
4 |
--------------------------------------------------------------------------------
/os-exec/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/razeencheng/demo-go/os-exec
2 |
3 | go 1.18
4 |
--------------------------------------------------------------------------------
/recover/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/razeencheng/demo-go/recover
2 |
3 | go 1.18
4 |
--------------------------------------------------------------------------------
/os-exec/testcmd/testcmd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razeencheng/demo-go/HEAD/os-exec/testcmd/testcmd
--------------------------------------------------------------------------------
/windows_api/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/razeencheng/demo-go/windows_api
2 |
3 | go 1.18
4 |
5 | require github.com/pkg/errors v0.9.1
6 |
--------------------------------------------------------------------------------
/swaggo-gin/Makefile:
--------------------------------------------------------------------------------
1 | build: swag
2 | go build -tags "doc"
3 | build_prod:
4 | go build
5 | swag:
6 | swag init --markdownFiles .
7 | run: build
8 | ./swaggo-gin
--------------------------------------------------------------------------------
/windows_api/go.sum:
--------------------------------------------------------------------------------
1 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
2 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
3 |
--------------------------------------------------------------------------------
/gin/key.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN EC PRIVATE KEY-----
2 | MHcCAQEEIEGYhoYypHmtEdwkQZqOp1ADqTaw5zSKek6zMaCHpE9ZoAoGCCqGSM49
3 | AwEHoUQDQgAEsLzd9fZ1JyfanPY6vrVrGmFeIMIJTDsaJsjxryEbJj/dLTvYHoJ+
4 | ps4yK6MnSd3T6dEKWipD7YB3NZo1oTYXug==
5 | -----END EC PRIVATE KEY-----
6 |
7 |
--------------------------------------------------------------------------------
/benchmark/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/hex"
5 | "fmt"
6 | )
7 |
8 | func EncodeA(b []byte) string {
9 | return fmt.Sprintf("%x", b)
10 | }
11 |
12 | func EncodeB(b []byte) string {
13 | return hex.EncodeToString(b)
14 | }
15 |
--------------------------------------------------------------------------------
/oauth2/public/index.tmpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Login with github
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/swaggo-gin/api.md:
--------------------------------------------------------------------------------
1 | # Test Example Makedown
2 |
3 | ### 关于使用说明
4 |
5 | 吧啦吧啦吧啦
6 |
7 | 
--------------------------------------------------------------------------------
/benchmark/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/razeencheng/demo-go/benchmark
2 |
3 | go 1.18
4 |
5 | require github.com/stretchr/testify v1.7.4
6 |
7 | require (
8 | github.com/davecgh/go-spew v1.1.1 // indirect
9 | github.com/pmezard/go-difflib v1.0.0 // indirect
10 | gopkg.in/yaml.v3 v3.0.1 // indirect
11 | )
12 |
--------------------------------------------------------------------------------
/grpc/README.md:
--------------------------------------------------------------------------------
1 | 在gRPC官网用了一句话来介绍:“一个高性能、开源的通用RPC框架”,同时介绍了其四大特点:
2 |
3 | * 定义简单
4 | * 支持多种编程语言多种平台
5 | * 快速启动和缩放
6 | * 双向流媒体和集成身份验证
7 |
8 |
9 |
10 | 在`gRPC在go中使用`系列中,关于其简介与性能我就不多介绍,相信在社区也有很多关于这些的讨论。这里我主要从三个层次来总结我以往在Go中使用gRPC的一些经验,主要分为:
11 |
12 | 1. Protocol Buffers语法与相关使用
13 | 2. gRPC实现简单通讯
14 | 3. gRPC服务认证与双向流通讯
--------------------------------------------------------------------------------
/swaggo-gin/doc.go:
--------------------------------------------------------------------------------
1 | //go:build doc
2 | // +build doc
3 |
4 | package main
5 |
6 | import (
7 | _ "github.com/razeencheng/demo-go/swaggo-gin/docs"
8 |
9 | swaggerfiles "github.com/swaggo/files"
10 | ginSwagger "github.com/swaggo/gin-swagger"
11 | )
12 |
13 | func init() {
14 | swagHandler = ginSwagger.WrapHandler(swaggerfiles.Handler)
15 | }
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.dll
4 | *.so
5 | *.dylib
6 |
7 | # Test binary, build with `go test -c`
8 | *.test
9 |
10 | # Output of the go coverage tool, specifically when used with LiteIDE
11 | *.out
12 |
13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
14 | .glide/
15 | .vscode/
16 | .idea/
17 | .DS_Store
18 | swaggo-gin/swaggo-gin
19 | restful-api/restful-api
--------------------------------------------------------------------------------
/grpc/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/razeencheng/demo-go/grpc
2 |
3 | go 1.18
4 |
5 | require (
6 | github.com/golang/protobuf v1.5.2
7 | golang.org/x/net v0.0.0-20220621193019-9d032be2e588
8 | google.golang.org/grpc v1.47.0
9 | )
10 |
11 | require (
12 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
13 | golang.org/x/text v0.3.8 // indirect
14 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
15 | google.golang.org/protobuf v1.27.1 // indirect
16 | )
17 |
--------------------------------------------------------------------------------
/benchmark/main_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/require"
7 | )
8 |
9 | var buf = []byte("skdjadialsdgasadasdhsakdjsahlskdjagloqweiqwo")
10 |
11 | func TestEqual(t *testing.T) {
12 | should := require.New(t)
13 | should.Equal(EncodeA(buf), EncodeB(buf))
14 | }
15 |
16 | func BenchmarkEncodeA(b *testing.B) {
17 | for i := 0; i < b.N; i++ {
18 | EncodeA(buf)
19 | }
20 | }
21 |
22 | func BenchmarkEncodeB(b *testing.B) {
23 | for i := 0; i < b.N; i++ {
24 | EncodeB(buf)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/gomod/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/coreos/etcd/pkg/transport"
5 | "github.com/google/certificate-transparency-go/tls"
6 | "github.com/qiniu/api.v7/auth/qbox"
7 | "go.etcd.io/etcd/clientv3"
8 | "google.golang.org/grpc"
9 | "qiniupkg.com/x/log.v7"
10 | )
11 |
12 | func main() {
13 |
14 | _ = transport.TLSInfo{}
15 |
16 | _ = clientv3.WatchResponse{}
17 |
18 | _, _ = clientv3.New(clientv3.Config{})
19 |
20 | _ = qbox.NewMac("", "")
21 |
22 | _ = tls.DigitallySigned{}
23 |
24 | _ = grpc.ClientConn{}
25 |
26 | log.Info("hello world")
27 | }
28 |
--------------------------------------------------------------------------------
/grpc/demo1/helloworld/hello_world.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | option go_package = "github.com/razeencheng/demo-go/grpc/demo1/helloworld";
4 |
5 | package helloworld;
6 |
7 | import "github.com/golang/protobuf/ptypes/any/any.proto";
8 |
9 | message HelloWorldRequest {
10 | string greeting = 1;
11 | map infos = 2;
12 | }
13 |
14 | message HelloWorldResponse {
15 | string reply = 1;
16 | repeated google.protobuf.Any details = 2;
17 | }
18 |
19 | service HelloWorldService {
20 | rpc SayHelloWorld(HelloWorldRequest) returns (HelloWorldResponse){}
21 | }
--------------------------------------------------------------------------------
/oauth2/public/welcome.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Hello
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/os-exec/testcmd/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "os"
7 | "time"
8 | )
9 |
10 | func main() {
11 |
12 | var (
13 | start bool
14 | e bool
15 | )
16 |
17 | flag.BoolVar(&start, "s", false, "start output")
18 | flag.BoolVar(&e, "e", false, "output err")
19 | flag.Parse()
20 |
21 | if start {
22 | for i := 5; i > 0; i-- {
23 | fmt.Fprintln(os.Stdout, "test cmd output", i)
24 | time.Sleep(1 * time.Second)
25 | }
26 | }
27 |
28 | if e {
29 | fmt.Fprintln(os.Stderr, "a err occur")
30 | os.Exit(1)
31 | }
32 |
33 | fmt.Fprintln(os.Stdout, "test cmd stdout")
34 | }
35 |
--------------------------------------------------------------------------------
/recover/recover.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "time"
7 | )
8 |
9 | func main() {
10 |
11 | ch := make(chan int, 10)
12 |
13 | for i := 2; i > 0; i-- {
14 | go func(i int) {
15 | defer func() {
16 | err := recover()
17 | if err != nil {
18 | log.Println(err)
19 | }
20 | }()
21 | for val := range ch {
22 | fmt.Println("---->", val, "Go", i)
23 | if val%2 == 1 && i == 1 {
24 | panic("BOOM BOOM")
25 | }
26 | time.Sleep(2 * time.Second)
27 | }
28 | }(i)
29 | }
30 |
31 | var i int
32 | for {
33 | ch <- i
34 | time.Sleep(1 * time.Second)
35 | fmt.Println(i, "<---")
36 | i++
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/grpc/demo2/helloworld/hello_world.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | option go_package = "github.com/razeencheng/demo-go/grpc/demo2/helloworld";
4 |
5 | package helloworld;
6 |
7 | import "github.com/golang/protobuf/ptypes/any/any.proto";
8 |
9 | message HelloWorldRequest {
10 | string greeting = 1;
11 | map infos = 2;
12 | }
13 |
14 | message HelloWorldResponse {
15 | string reply = 1;
16 | repeated google.protobuf.Any details = 2;
17 | }
18 |
19 | service HelloWorldService {
20 | rpc SayHelloWorld(HelloWorldRequest) returns (HelloWorldResponse){}
21 | }
22 |
23 | message HelloWorld {
24 | string msg = 1;
25 | }
26 |
27 | message Error {
28 | repeated string msg = 1;
29 | }
30 |
--------------------------------------------------------------------------------
/timer/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os"
5 | "os/signal"
6 | "runtime/trace"
7 | "sync/atomic"
8 | "syscall"
9 | "time"
10 | )
11 |
12 | func main(){
13 |
14 | trace.Start(os.Stderr)
15 | defer trace.Stop()
16 |
17 | sigs := make(chan os.Signal,1)
18 | signal.Notify(sigs,syscall.SIGINT,syscall.SIGTERM)
19 |
20 | //time.AfterFunc(time.Second, func() {
21 | // println("done")
22 | //})
23 |
24 |
25 | var num int64
26 |
27 | for i:=0; i< 1e3 ; i++ {
28 | time.AfterFunc(time.Second, func() {
29 | atomic.AddInt64(&num,1)
30 | })
31 | }
32 |
33 | t:= 0
34 | for i:=0;i<1e10; i++ {
35 | t ++
36 | }
37 | _ = t
38 |
39 | <- sigs
40 |
41 | // println(num,"timers created,",t,"iterations done")
42 | }
43 |
--------------------------------------------------------------------------------
/grpc/demo2/client/client.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "log"
6 |
7 | "google.golang.org/grpc"
8 |
9 | pb "github.com/razeencheng/demo-go/grpc/demo2/helloworld"
10 | )
11 |
12 | func main() {
13 | conn, err := grpc.Dial("localhost:8080", grpc.WithInsecure())
14 | if err != nil {
15 | log.Fatalf("Dial failed:%v", err)
16 | }
17 | defer conn.Close()
18 |
19 | client := pb.NewHelloWorldServiceClient(conn)
20 | resp1, err := client.SayHelloWorld(context.Background(), &pb.HelloWorldRequest{
21 | Greeting: "Hello Server 1 !!",
22 | Infos: map[string]string{"hello": "world"},
23 | })
24 |
25 | log.Printf("Resp1:%+v", resp1)
26 |
27 | resp2, err := client.SayHelloWorld(context.Background(), &pb.HelloWorldRequest{
28 | Greeting: "Hello Server 2 !!",
29 | })
30 |
31 | log.Printf("Resp2:%+v", resp2)
32 | }
33 |
--------------------------------------------------------------------------------
/grpc/demo3/helloworld/hello_world.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | option go_package = "github.com/razeencheng/demo-go/grpc/demo3/helloworld";
4 |
5 | package helloworld;
6 |
7 | import "github.com/golang/protobuf/ptypes/any/any.proto";
8 |
9 | message HelloWorldRequest {
10 | string greeting = 1;
11 | map infos = 2;
12 | }
13 |
14 | message HelloWorldResponse {
15 | string reply = 1;
16 | repeated google.protobuf.Any details = 2;
17 | }
18 |
19 | service HelloWorldService {
20 | rpc SayHelloWorld(HelloWorldRequest) returns (HelloWorldResponse){}
21 | rpc ListHello(HelloWorldRequest) returns (stream HelloWorldResponse) {}
22 | rpc SayMoreHello(stream HelloWorldRequest) returns (HelloWorldResponse) {}
23 | rpc SayHelloChat(stream HelloWorldRequest) returns (stream HelloWorldRequest) {}
24 | }
25 |
26 | message HelloWorld {
27 | string msg = 1;
28 | }
29 |
30 | message Error {
31 | repeated string msg = 1;
32 | }
33 |
--------------------------------------------------------------------------------
/json/tag/tag.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | )
7 |
8 | type Person struct {
9 | FirstName string `json:"first_name"`
10 | LastName string `json:"last_name"`
11 | Nickname string `json:"nickname,omitempty"`
12 | Sex int `json:"sex,string"`
13 | Age int `json:"age,omitempty"`
14 | AgeStr int `json:"age,omitempty,string"`
15 | Merried bool `json:"merried,omitempty"`
16 | Ms bool `json:"ms,omitempty,string"`
17 | Relation *Relation `json:"relation"`
18 | }
19 |
20 | type Relation struct {
21 | Mma *Person `json:"mma"`
22 | Son *Person `json:"son,omitempty"`
23 | }
24 |
25 | func main() {
26 | xiaoming := &Person{
27 | FirstName: "xiaoming",
28 | Nickname: "",
29 | AgeStr: 18,
30 | Ms: true,
31 | Relation: &Relation{},
32 | }
33 | buf, err := json.MarshalIndent(xiaoming, "", "")
34 | if err != nil {
35 | panic(err)
36 | }
37 | fmt.Println(string(buf))
38 | }
39 |
--------------------------------------------------------------------------------
/grpc/Makefile:
--------------------------------------------------------------------------------
1 |
2 | demo1_mac_gen:
3 | protoc -I $${GOPATH}/src --go_out=plugins=grpc:$${GOPATH}/src $${GOPATH}/src/github.com/razeencheng/demo-go/grpc/demo1/helloworld/hello_world.proto
4 |
5 | demo1_win_gen:
6 | protoc -I $$env:GOPATH\src --go_out=plugins=grpc:$$env:GOPATH\src $$env:GOPATH\src\github.com\razeencheng\demo-go\grpc\demo1\helloworld\hello_world.proto
7 |
8 |
9 |
10 | demo2_mac_gen:
11 | protoc -I $${GOPATH}/src --go_out=plugins=grpc:$${GOPATH}/src $${GOPATH}/src/github.com/razeencheng/demo-go/grpc/demo2/helloworld/hello_world.proto
12 |
13 | demo2_win_gen:
14 | protoc -I $$env:GOPATH\src --go_out=plugins=grpc:$$env:GOPATH\src $$env:GOPATH\src\github.com\razeencheng\demo-go\grpc\demo2\helloworld\hello_world.proto
15 |
16 |
17 | demo3_mac_gen:
18 | protoc -I $${GOPATH}/src --go_out=plugins=grpc:$${GOPATH}/src $${GOPATH}/src/github.com/razeencheng/demo-go/grpc/demo3/helloworld/hello_world.proto
19 |
20 | demo3_win_gen:
21 | protoc -I $$env:GOPATH\src --go_out=plugins=grpc:$$env:GOPATH\src $$env:GOPATH\src\github.com\razeencheng\demo-go\grpc\demo3\helloworld\hello_world.proto
--------------------------------------------------------------------------------
/gomod/go.mod:
--------------------------------------------------------------------------------
1 | module demo-go/gomod
2 |
3 | go 1.16
4 |
5 | replace qiniupkg.com/x => qiniupkg.com/x v1.7.8
6 |
7 | replace github.com/qiniu/x => github.com/qiniu/x v1.7.8
8 |
9 | replace go.etcd.io/etcd => go.etcd.io/etcd v3.3.20+incompatible
10 |
11 | replace github.com/coreos/bbolt v1.3.6 => go.etcd.io/bbolt v1.3.6
12 |
13 | replace github.com/coreos/etcd => github.com/coreos/etcd v3.3.20+incompatible
14 |
15 | replace google.golang.org/grpc => google.golang.org/grpc v1.26.0
16 |
17 | require (
18 | github.com/coreos/bbolt v1.3.6 // indirect
19 | github.com/coreos/etcd v3.3.10+incompatible
20 | github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
21 | github.com/google/certificate-transparency-go v1.1.1
22 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
23 | github.com/qiniu/api.v7 v7.2.5+incompatible
24 | github.com/qiniu/x v0.0.0-00010101000000-000000000000 // indirect
25 | github.com/soheilhy/cmux v0.1.5 // indirect
26 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
27 | go.etcd.io/etcd v0.0.0-20200513171258-e048e166ab9c
28 | google.golang.org/grpc v1.29.1
29 | qiniupkg.com/x v0.0.0-00010101000000-000000000000
30 | sigs.k8s.io/yaml v1.2.0 // indirect
31 | )
32 |
--------------------------------------------------------------------------------
/grpc/demo2/server/server.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "log"
6 | "net"
7 |
8 | "github.com/golang/protobuf/ptypes"
9 | "github.com/golang/protobuf/ptypes/any"
10 | "google.golang.org/grpc"
11 |
12 | pb "github.com/razeencheng/demo-go/grpc/demo2/helloworld"
13 | )
14 |
15 | type SayHelloServer struct{}
16 |
17 | func (s *SayHelloServer) SayHelloWorld(ctx context.Context, in *pb.HelloWorldRequest) (res *pb.HelloWorldResponse, err error) {
18 | log.Printf("Client Greeting:%s", in.Greeting)
19 | log.Printf("Client Info:%v", in.Infos)
20 |
21 | var an *any.Any
22 | if in.Infos["hello"] == "world" {
23 | an, err = ptypes.MarshalAny(&pb.HelloWorld{Msg: "Good Request"})
24 | } else {
25 | an, err = ptypes.MarshalAny(&pb.Error{Msg: []string{"Bad Request", "Wrong Info Msg"}})
26 | }
27 |
28 | if err != nil {
29 | return
30 | }
31 | return &pb.HelloWorldResponse{
32 | Reply: "Hello World !!",
33 | Details: []*any.Any{an},
34 | }, nil
35 | }
36 |
37 | func main() {
38 | lis, err := net.Listen("tcp", ":8080")
39 | if err != nil {
40 | panic(err)
41 | }
42 |
43 | grpcServer := grpc.NewServer()
44 | pb.RegisterHelloWorldServiceServer(grpcServer, &SayHelloServer{})
45 | grpcServer.Serve(lis)
46 | }
47 |
--------------------------------------------------------------------------------
/grpc/demo3/client/certs/root.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDizCCAnOgAwIBAgIRAMfjPkDKfELTo07l3A3cUSYwDQYJKoZIhvcNAQELBQAw
3 | XzELMAkGA1UEBhMCQ04xDjAMBgNVBAoTBU15U1NMMSwwKgYDVQQLEyNNeVNTTCBU
4 | ZXN0IFJvb3QgLSBGb3IgdGVzdCB1c2Ugb25seTESMBAGA1UEAxMJTXlTU0wuY29t
5 | MB4XDTE3MTExNjA1MzUzNVoXDTM3MTExNjA1MzUzNVowXzELMAkGA1UEBhMCQ04x
6 | DjAMBgNVBAoTBU15U1NMMSwwKgYDVQQLEyNNeVNTTCBUZXN0IFJvb3QgLSBGb3Ig
7 | dGVzdCB1c2Ugb25seTESMBAGA1UEAxMJTXlTU0wuY29tMIIBIjANBgkqhkiG9w0B
8 | AQEFAAOCAQ8AMIIBCgKCAQEAt2vNDivdBzQbYsatQNV7JoQ5z8nT0AVMBDbkb1zX
9 | tP6CyVp+nBdxMjqihrm1AsePciItsEmhYjxEi9JCp0BgXipHEN/Ecf2iNXwTIB8N
10 | X0oOg1pbIBa/ULscpc11JtGHzfHdydsqLKpM7dqdXqos9sn8Eh2+jAHAIcLjw8l7
11 | K4KpAS3ZL1NY7hCxDsXK2T03o6hqeiXIKsf4DPtQGbg3q0qmNluV0cMFWgsYsah0
12 | wyJNvAGGPtIK/2D40IjAFc+1yGcjbsgUAuhr1//DGYMgZNA3rDwPfqxJ/KMczdau
13 | 1Hy2P47QpidB2rZY5i2I12bwdtBFBLPtTYb0AoOAqIY4ewIDAQABo0IwQDAOBgNV
14 | HQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUmvGfuSUSEons
15 | cxi5l7zF+KAKflMwDQYJKoZIhvcNAQELBQADggEBALTy4qdRSUC7XGKnbN9JfQWH
16 | bxs0McAETrMlpz/HJJzBuQ1fB+qngxfTZxCzufQzEGkGxwwCm98EccyeMdokMiWK
17 | 0SKKmiavDiHImvnXYJ606UXZ8eKITp9C+F+pwWjIkYT2uO2mja1B1GbzkK1sKfFk
18 | fEZAcHSfES1K8HU/XWEUiTe0OijX9+oWtedi0MUp3enJvMjR3hiWJgSmukawNBj1
19 | a+Yvmia1M6z7KmSbQzL/jyq69QWmpO0dAaD0DVUB4/YFWHTw4J0qirS/dP0A8dAk
20 | CjWTnYPhCcO2uIcnqMt7zCVs5LXBK/XSwlAXKMvKT0uuzw9VxeMfEabflKu0By8=
21 | -----END CERTIFICATE-----
--------------------------------------------------------------------------------
/grpc/demo3/server/certs/root.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDizCCAnOgAwIBAgIRAMfjPkDKfELTo07l3A3cUSYwDQYJKoZIhvcNAQELBQAw
3 | XzELMAkGA1UEBhMCQ04xDjAMBgNVBAoTBU15U1NMMSwwKgYDVQQLEyNNeVNTTCBU
4 | ZXN0IFJvb3QgLSBGb3IgdGVzdCB1c2Ugb25seTESMBAGA1UEAxMJTXlTU0wuY29t
5 | MB4XDTE3MTExNjA1MzUzNVoXDTM3MTExNjA1MzUzNVowXzELMAkGA1UEBhMCQ04x
6 | DjAMBgNVBAoTBU15U1NMMSwwKgYDVQQLEyNNeVNTTCBUZXN0IFJvb3QgLSBGb3Ig
7 | dGVzdCB1c2Ugb25seTESMBAGA1UEAxMJTXlTU0wuY29tMIIBIjANBgkqhkiG9w0B
8 | AQEFAAOCAQ8AMIIBCgKCAQEAt2vNDivdBzQbYsatQNV7JoQ5z8nT0AVMBDbkb1zX
9 | tP6CyVp+nBdxMjqihrm1AsePciItsEmhYjxEi9JCp0BgXipHEN/Ecf2iNXwTIB8N
10 | X0oOg1pbIBa/ULscpc11JtGHzfHdydsqLKpM7dqdXqos9sn8Eh2+jAHAIcLjw8l7
11 | K4KpAS3ZL1NY7hCxDsXK2T03o6hqeiXIKsf4DPtQGbg3q0qmNluV0cMFWgsYsah0
12 | wyJNvAGGPtIK/2D40IjAFc+1yGcjbsgUAuhr1//DGYMgZNA3rDwPfqxJ/KMczdau
13 | 1Hy2P47QpidB2rZY5i2I12bwdtBFBLPtTYb0AoOAqIY4ewIDAQABo0IwQDAOBgNV
14 | HQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUmvGfuSUSEons
15 | cxi5l7zF+KAKflMwDQYJKoZIhvcNAQELBQADggEBALTy4qdRSUC7XGKnbN9JfQWH
16 | bxs0McAETrMlpz/HJJzBuQ1fB+qngxfTZxCzufQzEGkGxwwCm98EccyeMdokMiWK
17 | 0SKKmiavDiHImvnXYJ606UXZ8eKITp9C+F+pwWjIkYT2uO2mja1B1GbzkK1sKfFk
18 | fEZAcHSfES1K8HU/XWEUiTe0OijX9+oWtedi0MUp3enJvMjR3hiWJgSmukawNBj1
19 | a+Yvmia1M6z7KmSbQzL/jyq69QWmpO0dAaD0DVUB4/YFWHTw4J0qirS/dP0A8dAk
20 | CjWTnYPhCcO2uIcnqMt7zCVs5LXBK/XSwlAXKMvKT0uuzw9VxeMfEabflKu0By8=
21 | -----END CERTIFICATE-----
--------------------------------------------------------------------------------
/benchmark/go.sum:
--------------------------------------------------------------------------------
1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
5 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
6 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
7 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
8 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
9 | github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM=
10 | github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
11 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
12 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
13 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
14 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
15 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
16 |
--------------------------------------------------------------------------------
/swaggo-gin/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/gin-gonic/contrib/sessions"
5 | "github.com/gin-gonic/gin"
6 | )
7 |
8 | // 文档Handle
9 | var swagHandler gin.HandlerFunc
10 |
11 | // @title Swagger Example API
12 | // @version 1.0
13 | // @description This is a sample server celler server.
14 | // @termsOfService https://razeen.me
15 |
16 | // @contact.name Razeen
17 | // @contact.url https://razeen.me
18 | // @contact.email me@razeen.me
19 |
20 | // @tag.name TestTag1
21 | // @tag.description This is a test tag
22 | // @tag.docs.url https://razeen.me
23 | // @tag.docs.description This is my blog site
24 |
25 | // @license.name Apache 2.0
26 | // @license.url http://www.apache.org/licenses/LICENSE-2.0.html
27 |
28 | // @host 127.0.0.1:8080
29 | // @BasePath /api/v1
30 |
31 | // @schemes http https
32 | // @x-example-key {"key": "value"}
33 |
34 | // @description.markdown
35 |
36 | func main() {
37 |
38 | r := gin.Default()
39 | store := sessions.NewCookieStore([]byte("secret"))
40 | r.Use(sessions.Sessions("mysession", store))
41 |
42 | v1 := r.Group("/api/v1")
43 | {
44 | v1.GET("/hello", HandleHello)
45 | v1.POST("/login", HandleLogin)
46 | v1Auth := v1.Use(HandleAuth)
47 | {
48 | v1Auth.POST("/upload", HandleUpload)
49 | v1Auth.GET("/list", HandleList)
50 | v1Auth.GET("/file/:id", HandleGetFile)
51 | v1Auth.POST("/json", HandleJSON)
52 | }
53 | }
54 |
55 | if swagHandler != nil {
56 | r.GET("/swagger/*any", swagHandler)
57 | }
58 |
59 | r.Run(":8080")
60 | }
61 |
--------------------------------------------------------------------------------
/restful-api/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/razeencheng/demo-go/restful-api
2 |
3 | go 1.18
4 |
5 | require (
6 | github.com/gin-gonic/contrib v0.0.0-20201101042839-6a891bf89f19
7 | github.com/gin-gonic/gin v1.8.1
8 | )
9 |
10 | require (
11 | github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect
12 | github.com/gin-contrib/sse v0.1.0 // indirect
13 | github.com/go-playground/locales v0.14.0 // indirect
14 | github.com/go-playground/universal-translator v0.18.0 // indirect
15 | github.com/go-playground/validator/v10 v10.11.0 // indirect
16 | github.com/goccy/go-json v0.9.7 // indirect
17 | github.com/gomodule/redigo v2.0.0+incompatible // indirect
18 | github.com/gorilla/context v1.1.1 // indirect
19 | github.com/gorilla/securecookie v1.1.1 // indirect
20 | github.com/gorilla/sessions v1.2.1 // indirect
21 | github.com/json-iterator/go v1.1.12 // indirect
22 | github.com/leodido/go-urn v1.2.1 // indirect
23 | github.com/mattn/go-isatty v0.0.14 // indirect
24 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
25 | github.com/modern-go/reflect2 v1.0.2 // indirect
26 | github.com/pelletier/go-toml/v2 v2.0.2 // indirect
27 | github.com/ugorji/go/codec v1.2.7 // indirect
28 | golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
29 | golang.org/x/net v0.0.0-20220621193019-9d032be2e588 // indirect
30 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
31 | golang.org/x/text v0.3.8 // indirect
32 | google.golang.org/protobuf v1.28.0 // indirect
33 | gopkg.in/yaml.v2 v2.4.0 // indirect
34 | )
35 |
--------------------------------------------------------------------------------
/gin/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/razeencheng/demo-go/gin
2 |
3 | go 1.18
4 |
5 | require (
6 | github.com/gin-contrib/size v0.0.0-20220501091047-44dc10afe2e0
7 | github.com/gin-gonic/contrib v0.0.0-20201101042839-6a891bf89f19
8 | github.com/gin-gonic/gin v1.8.1
9 | )
10 |
11 | require (
12 | github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect
13 | github.com/gin-contrib/sse v0.1.0 // indirect
14 | github.com/go-playground/locales v0.14.0 // indirect
15 | github.com/go-playground/universal-translator v0.18.0 // indirect
16 | github.com/go-playground/validator/v10 v10.10.0 // indirect
17 | github.com/goccy/go-json v0.9.7 // indirect
18 | github.com/gomodule/redigo v2.0.0+incompatible // indirect
19 | github.com/gorilla/context v1.1.1 // indirect
20 | github.com/gorilla/securecookie v1.1.1 // indirect
21 | github.com/gorilla/sessions v1.2.1 // indirect
22 | github.com/json-iterator/go v1.1.12 // indirect
23 | github.com/leodido/go-urn v1.2.1 // indirect
24 | github.com/mattn/go-isatty v0.0.14 // indirect
25 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
26 | github.com/modern-go/reflect2 v1.0.2 // indirect
27 | github.com/pelletier/go-toml/v2 v2.0.1 // indirect
28 | github.com/ugorji/go/codec v1.2.7 // indirect
29 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
30 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect
31 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
32 | golang.org/x/text v0.3.8 // indirect
33 | google.golang.org/protobuf v1.28.0 // indirect
34 | gopkg.in/yaml.v2 v2.4.0 // indirect
35 | )
36 |
--------------------------------------------------------------------------------
/grpc/demo3/server/certs/test_server.key:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIIEowIBAAKCAQEA0d8HO5kgssjpu3bBpaOWKUepnelB1Be84/24lPyq1IpdePXU
3 | xCZbjLd082g0gocShUGHyYAEEDJ3Gng3e6iBhaj90RDbUYBQO09anKCRezbCh5g5
4 | Ei4g72PwPVxIym/i4h6GH6zRExXjtwMCS3zNvijTYB24vvaDGLdQAzqHtkRBzGis
5 | vGFGp+Dq9YDUnvDtZUdEceEUbG+8oW6VI5LrRCNS4xQkPEQDGIVdGQ53EZwAw4oz
6 | wjbct8kpO/oIDLTnFmm9y0iKr6SL1pZNsEPdc8FE3bsfb84VjckunCqjjA7NtVJQ
7 | w4RctvabaEXtcdMMIXgzinOg6kVR+p8ipI4fKwIDAQABAoIBABUCr/WuedCMPqIO
8 | 3IoZm3Hcg8WUUYvLKJTmlLENroiCpaLzcW6FrIqk3ydzGjarERaYHeD1WPLKKpRS
9 | zrMphnX8GhTxDBPXtbCKXDbr+ESOkeEDQ0tnpNJyfd2WvI7PrrYWejT3lyLDAT4U
10 | Jgmqn+pWqfqoXxN3GF7EE1WPaYC8nDNNfubMa7xtZ9DsFpKkoKSyjob+5ko2QvLU
11 | StKLPqZHvAozqaXywngkZcxPe/MQ3BBvCMW3sROcUC4TkkeUUQvZMbWYauwwZPO1
12 | Y2j4VbVpPIVrJw34tecKwhA04lRMfthFg4ofLOYd0Fo9rDRvm7/dJv1rq9KYCHxx
13 | C5GBWykCgYEA5FeqK1NWnBVbxnWAz7MCtITLMQoXsETMT+AAJMFqKRgjWUayZJ+R
14 | pFenNarght5Cg0x60//S/AgsW0E1XYRfmP2ynRW5PlUK/5a516EGNCO8D8ysL+3T
15 | qPoJVbQve2ppEinB6iKEBCXXfnHIEoh3UZcO5L4heAaNtbo3Zl31hmcCgYEA60qd
16 | uNgTWLey+tDFoNmLiIo1A7LNcCEPz9HjRhgFhGyZdgsMu6+4yrij3OCmBuNKinxc
17 | 200pH7pdzElcbxoBPCRBYyp0PP743UloTHOvLbZKMc1zvaMnLuYNc2urncPQm7cY
18 | O0Eu5Oei+igBSnYG7zJ3thi7dgTFf2/8qyAMfp0CgYBdT0mVrNeersezIL9DoNZB
19 | KGbUIlityl9nYJUOcA9OvbNsPwyVO/PLIZqNvsdDtb2eY/lpeoggP8LIg6woC/j4
20 | zutXb9IkG6KD8Xb+G6oqUlP+bNgMOfadfZek8x3YJBLNvkykvfgOrdwSqrJkiGmh
21 | 6MXISb3pi1wLYA5VgZ3cjwKBgBZ7XLKqwr55XvqkNB34a2KygfpGfWa3YgFjdqnd
22 | 5bkPf2JOD/tnAOst1UpnlLWXximRVHYH22QStJ+uasya2X9bn3vQNKcXXcQXHYYg
23 | j554ioJTtTlre3T5ulNK+GzspuQaDJCs28Q3ddUdOXJZ8LOuSM33cwIF1rEkjCn1
24 | t/vpAoGBAMc/FAy+xR0rpSwy9BhCyolA6TsJJfw4T9A3HPT1Y5D8X4wyeRMZEN2x
25 | 0HARojnNYlHUEF2diZvDXCHya/TIT8NCxvAqJ2h6jADjpwh8PsTcqCJ8KosCq5P0
26 | nAGKgcLt4KUS0HimRt4cBfsj/o9QnKqkeds4zPvh3fvTtDRPwno3
27 | -----END RSA PRIVATE KEY-----
28 |
--------------------------------------------------------------------------------
/grpc/demo3/client/certs/test_client.key:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIIEpAIBAAKCAQEA3ucIeuuNCk03p1dqs7t5K4DLl9A+JJmn6JAF1xGJn1ItMc/B
3 | HrI99DnMzRVWBXBbSxElXKgmjGdteAA8RXpzpWBlm0DBuQl8RpipSXxrInvE2tfe
4 | NtkAxmUW6I4b0Wq3DPZQoUAlZnqsWbVMt4BGUOYFRizMhEwEIaKfyznVtuxkOX8l
5 | ZYx1cZl1YMv6G81hNTU0VvPk4xhTJIvcZqFcytLQbP2LgQvH8VKH6ZKbFbdcWksT
6 | vk85F3yOXGs9hFHNZZ0nLOKw8Ycc3VcO1ymEMtA4vTeW8tQX5vnM6Gfi+AQHuKa1
7 | YJeseb7MedWohny68EfxxbMdDuD9CnVGH1dIhwIDAQABAoIBAQCerL5nqli7lZ5h
8 | nKQTkAhAxK2pw48JROy3bBcMM6rRZJFRnUAvltjti1vUPjT/KC2E8Y60N+tBFbti
9 | qmA9YV8DwexyqZV+IFd8dd5EtrIZWRwYYYUJbE7V7E5ze/4nhJ0MOCfLsjstiXQ5
10 | ZAtZsZHcsvHcM7XfE7H5M6gm5mPRk3+q4VoJyLf8z1FDVMdIIK0s1k+QbBL412DL
11 | QQR4r8jkepCpGXNyGsrRCuPjZfZZYO7Ks+C9NgXtPZiK4rXVPL7ZciZko38CfDcu
12 | /tOZzrsnV3jmB2kz8MNVCuEUX5aSXT0AfC78oiRPfnEP9dv7cvwyBZtKNyfi2S2z
13 | 81dgDJ/xAoGBAOYf0YVWOddL5Mou0VmNQIdU0alkGwmo2bf4t5Bhp+EWLzsBVttS
14 | 5ldjOuD8KDDM9HDAa+K2Ruq395kwjsA8+qsRZOPDiOAscLTbgTaG1fhd1f029uJd
15 | +pAmVFV+m1xbOEddhgGvCij3iFbvKdM0W4AGxXE7fhV59U7KuqiuUp9pAoGBAPf3
16 | VT3MMpm6Ite/rCIXT5mVkQkzzthm6jf3y0b9OwPfPtuOSVcj0sM7wbn6UTPLEqDS
17 | ZJ/R5VWuSBDrBvZ40P2Vt2kyyRhGd2KxvKIJAuTO9yW3Dieo3LeIHWQlWhaKKXkL
18 | VIFQwt9q9A3UQEsHFHO9Ioha08FcRk/M6uEzx5pvAoGAcq9GxtGbD93lzpEYii2o
19 | DAnMV0PpaZ35qwLL0Kuqc7WPojNfqvwciU6NqFRiXze1Vn+/BIRcwVsfjPuzGMEL
20 | F7gadJwdGcNsA+Yk9hyqhBWXsJL05Ql69t3zR4xKNvPLD13fi/VE9feuvcyBJp3A
21 | QASf33eLtX5LL5I/BhOiX4kCgYEAqy6BFz9vszaPtTeqIoLLPfDGBn9QjY5GpUqY
22 | d7J72kl1AGcy9EhTyNno/HX1Nvc7LfDw7HAfjU3ajGtkDCUNyfJgguw/bVXAN08S
23 | NR5ZdBH5Bn1f9Tsa3EzIVYl/rs3Eob6ToQ5a6ZfRUfa0R9dkZB4ux8lEJFmKZK7H
24 | e56eblsCgYAZzii5W/InmoO9Vax3v9PzxF6vTchYsZDXso5LhvGl4fu1iF1dklVw
25 | AdLt0lDIV4tBgTqRkZgB7MStW/x8nbjOfMki4AM7R+55Zmk3/rvlsfZFlwcO9cHv
26 | p5fF1aRmhOVy/alXjEMqmCLIuZpz336C5WecwX/1nPnQ7Docg2gD7g==
27 | -----END RSA PRIVATE KEY-----
28 |
--------------------------------------------------------------------------------
/recover/README.md:
--------------------------------------------------------------------------------
1 | # [Go学习笔记(二) | 我对recover的一点误解](https://razeen.me/post/daily-go-recover.html)
2 |
3 | 在golang的官方介绍中是这么介绍**Recover**函数的。
4 |
5 |
6 |
7 | ```doc
8 | Recover is a built-in function that regains control of a panicking goroutine. Recover is only useful inside deferred functions. During normal execution, a call to recover will return nil and have no other effect. If the current goroutine is panicking, a call to recover will capture the value given to panic and resume normal execution.
9 | ```
10 |
11 | 也就是说当一个协程发生panic时,recover函数会捕捉到panic同时恢复正常的顺序。
12 |
13 |
14 |
15 | 像如下这样的代码:
16 |
17 | ``` golang
18 | package main
19 |
20 | import (
21 | "fmt"
22 | "log"
23 | "time"
24 | )
25 |
26 | func main() {
27 |
28 | ch := make(chan int, 10)
29 |
30 | for i := 2; i > 0; i-- {
31 | go func(i int) {
32 | defer func() {
33 | err := recover()
34 | if err != nil {
35 | log.Println(err)
36 | }
37 | }()
38 | for val := range ch {
39 | fmt.Println("---->", val, "Go", i)
40 | if val%2 == 1 && i == 1 {
41 | panic("BOOM BOOM")
42 | }
43 | time.Sleep(2 * time.Second)
44 | }
45 | }(i)
46 | }
47 |
48 | var i int
49 | for {
50 | ch <- i
51 | time.Sleep(1 * time.Second)
52 | fmt.Println(i, "<---")
53 | i++
54 | }
55 | }
56 | ```
57 |
58 | 我们向通道中写入,两个协成在接收,同时设计一个协程在一定的时候panic。那么在panic后,再recover,那在该协程的管道还能接收么?
59 |
60 | 过去,我一直以为,recover函数会重新恢复,该协程会类似重启一般==。
61 |
62 | ```bash
63 | $ go run recover.go
64 | ----> 0 Go 2
65 | 0 <---
66 | ----> 1 Go 1
67 | 2018/01/21 21:52:54 BOOM BOOM
68 | 1 <---
69 | ----> 2 Go 2
70 | 2 <---
71 | 3 <---
72 | ----> 3 Go 2
73 | 4 <---
74 | ----> 4 Go 2
75 | 5 <---
76 | 6 <---
77 | ----> 5 Go 2
78 | 7 <---
79 | 8 <---
80 | ----> 6 Go 2
81 | ...
82 | ```
83 |
84 | 但从执行的结果看:
85 |
86 | 只有一个协程在工作,另外一个还是挂了。这时,我才意识到,recover函数并没有恢复原有的协程。只是当该协程panic后会执行defer。而在defer中,recover函数将panic拦截下来了,不会向外面抛出,从而导致其他协程的执行并不受到影响。但,已经panic的协程还是挂了。
--------------------------------------------------------------------------------
/gin/cert.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIICTDCCAfKgAwIBAgIQCT2hVW+1RvCyxHrquA7xCjAKBggqhkjOPQQDAjBeMQsw
3 | CQYDVQQGEwJDTjEOMAwGA1UEChMFTXlTU0wxKzApBgNVBAsTIk15U1NMIFRlc3Qg
4 | RUNDIC0gRm9yIHRlc3QgdXNlIG9ubHkxEjAQBgNVBAMTCU15U1NMLmNvbTAeFw0x
5 | ODAzMTYwODAwMDVaFw0xOTAzMTYwODAwMDVaMCExCzAJBgNVBAYTAkNOMRIwEAYD
6 | VQQDEwlsb2NhbGhvc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASwvN319nUn
7 | J9qc9jq+tWsaYV4gwglMOxomyPGvIRsmP90tO9gegn6mzjIroydJ3dPp0QpaKkPt
8 | gHc1mjWhNhe6o4HOMIHLMA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUEFjAUBggrBgEF
9 | BQcDAQYIKwYBBQUHAwIwHwYDVR0jBBgwFoAUWxGyVxD0fBhTy3tH4eKznRFXFCYw
10 | YwYIKwYBBQUHAQEEVzBVMCEGCCsGAQUFBzABhhVodHRwOi8vb2NzcC5teXNzbC5j
11 | b20wMAYIKwYBBQUHMAKGJGh0dHA6Ly9jYS5teXNzbC5jb20vbXlzc2x0ZXN0ZWNj
12 | LmNydDAUBgNVHREEDTALgglsb2NhbGhvc3QwCgYIKoZIzj0EAwIDSAAwRQIgack7
13 | wDoc/nmD9zIWa8QdbQGKwWONPwDue4cIeDQKhcQCIQDwfasEIe7/vWtjNNZenkJH
14 | KhwjJ6SmdRl6wA6sgYT+AQ==
15 | -----END CERTIFICATE-----
16 | -----BEGIN CERTIFICATE-----
17 | MIIC8TCCAdmgAwIBAgIRALLkLesqekT2roCALo98PuswDQYJKoZIhvcNAQELBQAw
18 | XzELMAkGA1UEBhMCQ04xDjAMBgNVBAoTBU15U1NMMSwwKgYDVQQLEyNNeVNTTCBU
19 | ZXN0IFJvb3QgLSBGb3IgdGVzdCB1c2Ugb25seTESMBAGA1UEAxMJTXlTU0wuY29t
20 | MB4XDTE3MTExNjA1MzUzNVoXDTI3MTExNjA1MzUzNVowXjELMAkGA1UEBhMCQ04x
21 | DjAMBgNVBAoTBU15U1NMMSswKQYDVQQLEyJNeVNTTCBUZXN0IEVDQyAtIEZvciB0
22 | ZXN0IHVzZSBvbmx5MRIwEAYDVQQDEwlNeVNTTC5jb20wWTATBgcqhkjOPQIBBggq
23 | hkjOPQMBBwNCAARVzwT3bIs+d8QIISfVGWXwsWcFbxIFd/bhWj/rp1RDmAmQIVEe
24 | wRa/pQxCe575ClwV5fgRFRxbZ1A2nsUV6nN5o3QwcjAOBgNVHQ8BAf8EBAMCAYYw
25 | DwYDVR0lBAgwBgYEVR0lADAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFJrx
26 | n7klEhKJ7HMYuZe8xfigCn5TMB0GA1UdDgQWBBRbEbJXEPR8GFPLe0fh4rOdEVcU
27 | JjANBgkqhkiG9w0BAQsFAAOCAQEAs07fxWwDfhuAhxl31QjVVV7RBAUzLr/PLUAX
28 | y32r/WJhgq9Uio3b/EIv52EpFnF/uX4D0zppW4VDh18mo9ALeGAfV69MKqP2m0M4
29 | 5khVVLj1Xq+Clgvx+WxdMLgGsvGE2WwjagPVcwpH6TPUpn2mPwvpA3TW1ajHCH1D
30 | bp5ZPLC51FD5EUx1HfRRsO3m6nhtxM0fwTFSjTp2Gai87tY5p6zToiyS2KDKApYE
31 | OXuog0ixQhBv2w3l+kX2MbB1WWhQfpD+O8tWn0A/IxXgjTep3a3uybk8t9j7LyYM
32 | lPMMWy7ciA3RO4+UJ9lftpWiRT4b8hmwWrpnai2JmMVpih5cNA==
33 | -----END CERTIFICATE-----
34 |
--------------------------------------------------------------------------------
/swaggo-gin/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/razeencheng/demo-go/swaggo-gin
2 |
3 | go 1.18
4 |
5 | require (
6 | github.com/gin-gonic/contrib v0.0.0-20201101042839-6a891bf89f19
7 | github.com/gin-gonic/gin v1.8.1
8 | github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe
9 | github.com/swaggo/gin-swagger v1.5.0
10 | github.com/swaggo/swag v1.8.2
11 | )
12 |
13 | require (
14 | github.com/KyleBanks/depth v1.2.1 // indirect
15 | github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect
16 | github.com/gin-contrib/sse v0.1.0 // indirect
17 | github.com/go-openapi/jsonpointer v0.19.5 // indirect
18 | github.com/go-openapi/jsonreference v0.20.0 // indirect
19 | github.com/go-openapi/spec v0.20.6 // indirect
20 | github.com/go-openapi/swag v0.21.1 // indirect
21 | github.com/go-playground/locales v0.14.0 // indirect
22 | github.com/go-playground/universal-translator v0.18.0 // indirect
23 | github.com/go-playground/validator/v10 v10.11.0 // indirect
24 | github.com/goccy/go-json v0.9.7 // indirect
25 | github.com/gomodule/redigo v2.0.0+incompatible // indirect
26 | github.com/gorilla/context v1.1.1 // indirect
27 | github.com/gorilla/securecookie v1.1.1 // indirect
28 | github.com/gorilla/sessions v1.2.1 // indirect
29 | github.com/josharian/intern v1.0.0 // indirect
30 | github.com/json-iterator/go v1.1.12 // indirect
31 | github.com/leodido/go-urn v1.2.1 // indirect
32 | github.com/mailru/easyjson v0.7.7 // indirect
33 | github.com/mattn/go-isatty v0.0.14 // indirect
34 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
35 | github.com/modern-go/reflect2 v1.0.2 // indirect
36 | github.com/pelletier/go-toml/v2 v2.0.2 // indirect
37 | github.com/ugorji/go/codec v1.2.7 // indirect
38 | golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
39 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
40 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
41 | golang.org/x/text v0.3.8 // indirect
42 | golang.org/x/tools v0.1.12 // indirect
43 | google.golang.org/protobuf v1.28.0 // indirect
44 | gopkg.in/yaml.v2 v2.4.0 // indirect
45 | )
46 |
--------------------------------------------------------------------------------
/grpc/demo3/client/certs/test_client.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIID3DCCAsSgAwIBAgIQP1gd4oJ5QxeYSFtS2qjQnzANBgkqhkiG9w0BAQsFADBe
3 | MQswCQYDVQQGEwJDTjEOMAwGA1UEChMFTXlTU0wxKzApBgNVBAsTIk15U1NMIFRl
4 | c3QgUlNBIC0gRm9yIHRlc3QgdXNlIG9ubHkxEjAQBgNVBAMTCU15U1NMLmNvbTAe
5 | Fw0xODA4MDUwNzQ5NDJaFw0yMzA4MDQwNzQ5NDJaMCgxCzAJBgNVBAYTAkNOMRkw
6 | FwYDVQQDExBjbGllbnQucmF6ZWVuLm1lMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
7 | MIIBCgKCAQEA3ucIeuuNCk03p1dqs7t5K4DLl9A+JJmn6JAF1xGJn1ItMc/BHrI9
8 | 9DnMzRVWBXBbSxElXKgmjGdteAA8RXpzpWBlm0DBuQl8RpipSXxrInvE2tfeNtkA
9 | xmUW6I4b0Wq3DPZQoUAlZnqsWbVMt4BGUOYFRizMhEwEIaKfyznVtuxkOX8lZYx1
10 | cZl1YMv6G81hNTU0VvPk4xhTJIvcZqFcytLQbP2LgQvH8VKH6ZKbFbdcWksTvk85
11 | F3yOXGs9hFHNZZ0nLOKw8Ycc3VcO1ymEMtA4vTeW8tQX5vnM6Gfi+AQHuKa1YJes
12 | eb7MedWohny68EfxxbMdDuD9CnVGH1dIhwIDAQABo4HLMIHIMA4GA1UdDwEB/wQE
13 | AwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAjAfBgNVHSMEGDAWgBQogSYF0TQaP8Fz
14 | D7uTzxUcPwO/fzBjBggrBgEFBQcBAQRXMFUwIQYIKwYBBQUHMAGGFWh0dHA6Ly9v
15 | Y3NwLm15c3NsLmNvbTAwBggrBgEFBQcwAoYkaHR0cDovL2NhLm15c3NsLmNvbS9t
16 | eXNzbHRlc3Ryc2EuY3J0MBsGA1UdEQQUMBKCEGNsaWVudC5yYXplZW4ubWUwDQYJ
17 | KoZIhvcNAQELBQADggEBAA8jhr1/Lf5fmH4gCsRNSQ3ur19UM8F0Q0iXCeBIJUg0
18 | lL57UIR2phIN5PpsaLRlIfsR0EicQaEFJBShskqFCH5AWhxFo2+obBTu9KPSB10o
19 | uey8nOHqqV0yrhqZJr9NIU3QqGtNRn1H8JKHYVGSgThLBSq/cFwwhU4A8lxN1Pxt
20 | I3/RtwY8dPY6TuaRGNmyHOftJSPseASx+79IqrmCmVM2prNBJCdmkH/bk30ujroi
21 | tGGkBlf3FYQhFX7vWLQaXu7ZjOt2sh5RruY63bbS/uGAxzS4QYGjpgaFlZA98+LQ
22 | X48DNJpvoiXeDFZfLIaorTRoNDSYUj3G6OMbcRobOnM=
23 | -----END CERTIFICATE-----
24 | -----BEGIN CERTIFICATE-----
25 | MIIDuzCCAqOgAwIBAgIQSEIWDPfWTDKZcWNyL2O+fjANBgkqhkiG9w0BAQsFADBf
26 | MQswCQYDVQQGEwJDTjEOMAwGA1UEChMFTXlTU0wxLDAqBgNVBAsTI015U1NMIFRl
27 | c3QgUm9vdCAtIEZvciB0ZXN0IHVzZSBvbmx5MRIwEAYDVQQDEwlNeVNTTC5jb20w
28 | HhcNMTcxMTE2MDUzNTM1WhcNMjcxMTE2MDUzNTM1WjBeMQswCQYDVQQGEwJDTjEO
29 | MAwGA1UEChMFTXlTU0wxKzApBgNVBAsTIk15U1NMIFRlc3QgUlNBIC0gRm9yIHRl
30 | c3QgdXNlIG9ubHkxEjAQBgNVBAMTCU15U1NMLmNvbTCCASIwDQYJKoZIhvcNAQEB
31 | BQADggEPADCCAQoCggEBAMBOtZk0uzdG4dcIIdcAdSSYDbua0Bdd6N6s4hZaCOup
32 | q7G7lwXkCyViTYAFa3wZ0BMQ4Bl9Q4j82R5IaoqG7WRIklwYnQh4gZ14uRde6Mr8
33 | yzvPRbAXKVoVh4NPqpE6jWMTP38mh94bKc+ITAE5QBRhCTQ0ah2Hq846ZiDAj6sY
34 | hMJuhUWegVGd0vh0rvtzvYNx7NGyxzoj6MxkDiYfFiuBhF2R9Tmq2UW9KCZkEBVL
35 | Q/YKQuvZZKFqR7WUU8GpCwzUm1FZbKtaCyRRvzLa5otghU2teKS5SKVI+Tpxvasp
36 | fu4eXBvveMgyWwDpKlzLCLgvoC9YNpbmdiVxNNkjwNsCAwEAAaN0MHIwDgYDVR0P
37 | AQH/BAQDAgGGMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAfBgNV
38 | HSMEGDAWgBSa8Z+5JRISiexzGLmXvMX4oAp+UzAdBgNVHQ4EFgQUKIEmBdE0Gj/B
39 | cw+7k88VHD8Dv38wDQYJKoZIhvcNAQELBQADggEBAEl01ufit9rUeL5kZ31ox2vq
40 | 648azH/r/GR1S+mXci0Mg6RrDdLzUO7VSf0JULJf98oEPr9fpIZuRTyWcxiP4yh0
41 | wVd35OIQBTToLrMOWYWuApU4/YLKvg4A86h577kuYeSsWyf5kk0ngXsL1AFMqjOk
42 | Tc7p8PuW68S5/88Pe+Bq3sAaG3U5rousiTIpoN/osq+GyXisgv5jd2M4YBtl/NlD
43 | ppZs5LAOjct+Aaofhc5rNysonKjkd44K2cgBkbpOMj0dbVNKyL2/2I0zyY1FU2Mk
44 | URUHyMW5Qd5Q9g6Y4sDOIm6It9TF7EjpwMs42R30agcRYzuUsN72ZFBYFJwnBX8=
45 | -----END CERTIFICATE-----
--------------------------------------------------------------------------------
/grpc/demo3/server/certs/test_server.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIID5zCCAs+gAwIBAgIRANDUuMTieUHqoJRd+L1RWfQwDQYJKoZIhvcNAQELBQAw
3 | XjELMAkGA1UEBhMCQ04xDjAMBgNVBAoTBU15U1NMMSswKQYDVQQLEyJNeVNTTCBU
4 | ZXN0IFJTQSAtIEZvciB0ZXN0IHVzZSBvbmx5MRIwEAYDVQQDEwlNeVNTTC5jb20w
5 | HhcNMTgwODA1MDc0OTAzWhcNMjMwODA0MDc0OTAzWjAoMQswCQYDVQQGEwJDTjEZ
6 | MBcGA1UEAxMQc2VydmVyLnJhemVlbi5tZTCCASIwDQYJKoZIhvcNAQEBBQADggEP
7 | ADCCAQoCggEBANHfBzuZILLI6bt2waWjlilHqZ3pQdQXvOP9uJT8qtSKXXj11MQm
8 | W4y3dPNoNIKHEoVBh8mABBAydxp4N3uogYWo/dEQ21GAUDtPWpygkXs2woeYORIu
9 | IO9j8D1cSMpv4uIehh+s0RMV47cDAkt8zb4o02AduL72gxi3UAM6h7ZEQcxorLxh
10 | Rqfg6vWA1J7w7WVHRHHhFGxvvKFulSOS60QjUuMUJDxEAxiFXRkOdxGcAMOKM8I2
11 | 3LfJKTv6CAy05xZpvctIiq+ki9aWTbBD3XPBRN27H2/OFY3JLpwqo4wOzbVSUMOE
12 | XLb2m2hF7XHTDCF4M4pzoOpFUfqfIqSOHysCAwEAAaOB1TCB0jAOBgNVHQ8BAf8E
13 | BAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB8GA1UdIwQYMBaA
14 | FCiBJgXRNBo/wXMPu5PPFRw/A79/MGMGCCsGAQUFBwEBBFcwVTAhBggrBgEFBQcw
15 | AYYVaHR0cDovL29jc3AubXlzc2wuY29tMDAGCCsGAQUFBzAChiRodHRwOi8vY2Eu
16 | bXlzc2wuY29tL215c3NsdGVzdHJzYS5jcnQwGwYDVR0RBBQwEoIQc2VydmVyLnJh
17 | emVlbi5tZTANBgkqhkiG9w0BAQsFAAOCAQEAoKWnuLCLtrgcWEwh8jIZXpbrmUWj
18 | VmXVyOJw/TuF3UiZlAwyb8Cb0nArlVdMkdwhWUCtl6ZktMTTeCMeylJYY04obvEZ
19 | V4mIFCcTYFHJsW8m7SK2QVS2ssFIec/+xaVptzQB5+DUuD/Ldn2g1QslzRX4DFa3
20 | 30b3HKLLWQBWqa+lfMaGrOiTbWqEPt4cwWsKhFEQ9j3IFoMvEn+l/CozVEPPbsN2
21 | lJRlA7V1Xf/J1khjWHFDNUSivKuTw/1//jbKSFQ0bevqBlUEkqdwVzenRG0VU9cy
22 | j74s89EiEaAMm/MFTuSJrZrrkZLJfK7TDThf2kPqykVtWExJ+GpaBpQZOg==
23 | -----END CERTIFICATE-----
24 | -----BEGIN CERTIFICATE-----
25 | MIIDuzCCAqOgAwIBAgIQSEIWDPfWTDKZcWNyL2O+fjANBgkqhkiG9w0BAQsFADBf
26 | MQswCQYDVQQGEwJDTjEOMAwGA1UEChMFTXlTU0wxLDAqBgNVBAsTI015U1NMIFRl
27 | c3QgUm9vdCAtIEZvciB0ZXN0IHVzZSBvbmx5MRIwEAYDVQQDEwlNeVNTTC5jb20w
28 | HhcNMTcxMTE2MDUzNTM1WhcNMjcxMTE2MDUzNTM1WjBeMQswCQYDVQQGEwJDTjEO
29 | MAwGA1UEChMFTXlTU0wxKzApBgNVBAsTIk15U1NMIFRlc3QgUlNBIC0gRm9yIHRl
30 | c3QgdXNlIG9ubHkxEjAQBgNVBAMTCU15U1NMLmNvbTCCASIwDQYJKoZIhvcNAQEB
31 | BQADggEPADCCAQoCggEBAMBOtZk0uzdG4dcIIdcAdSSYDbua0Bdd6N6s4hZaCOup
32 | q7G7lwXkCyViTYAFa3wZ0BMQ4Bl9Q4j82R5IaoqG7WRIklwYnQh4gZ14uRde6Mr8
33 | yzvPRbAXKVoVh4NPqpE6jWMTP38mh94bKc+ITAE5QBRhCTQ0ah2Hq846ZiDAj6sY
34 | hMJuhUWegVGd0vh0rvtzvYNx7NGyxzoj6MxkDiYfFiuBhF2R9Tmq2UW9KCZkEBVL
35 | Q/YKQuvZZKFqR7WUU8GpCwzUm1FZbKtaCyRRvzLa5otghU2teKS5SKVI+Tpxvasp
36 | fu4eXBvveMgyWwDpKlzLCLgvoC9YNpbmdiVxNNkjwNsCAwEAAaN0MHIwDgYDVR0P
37 | AQH/BAQDAgGGMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAfBgNV
38 | HSMEGDAWgBSa8Z+5JRISiexzGLmXvMX4oAp+UzAdBgNVHQ4EFgQUKIEmBdE0Gj/B
39 | cw+7k88VHD8Dv38wDQYJKoZIhvcNAQELBQADggEBAEl01ufit9rUeL5kZ31ox2vq
40 | 648azH/r/GR1S+mXci0Mg6RrDdLzUO7VSf0JULJf98oEPr9fpIZuRTyWcxiP4yh0
41 | wVd35OIQBTToLrMOWYWuApU4/YLKvg4A86h577kuYeSsWyf5kk0ngXsL1AFMqjOk
42 | Tc7p8PuW68S5/88Pe+Bq3sAaG3U5rousiTIpoN/osq+GyXisgv5jd2M4YBtl/NlD
43 | ppZs5LAOjct+Aaofhc5rNysonKjkd44K2cgBkbpOMj0dbVNKyL2/2I0zyY1FU2Mk
44 | URUHyMW5Qd5Q9g6Y4sDOIm6It9TF7EjpwMs42R30agcRYzuUsN72ZFBYFJwnBX8=
45 | -----END CERTIFICATE-----
--------------------------------------------------------------------------------
/grpc/demo3/client/client.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "crypto/tls"
6 | "crypto/x509"
7 | "fmt"
8 | "io"
9 | "io/ioutil"
10 | "log"
11 |
12 | "google.golang.org/grpc"
13 | "google.golang.org/grpc/credentials"
14 |
15 | pb "github.com/razeencheng/demo-go/grpc/demo3/helloworld"
16 | )
17 |
18 | func main() {
19 | cert, err := tls.LoadX509KeyPair("certs/test_client.pem", "certs/test_client.key")
20 | if err != nil {
21 | panic(err)
22 | }
23 |
24 | // 将根证书加入证书池
25 | certPool := x509.NewCertPool()
26 | bs, err := ioutil.ReadFile("certs/root.pem")
27 | if err != nil {
28 | panic(err)
29 | }
30 |
31 | if !certPool.AppendCertsFromPEM(bs) {
32 | panic("cc")
33 | }
34 |
35 | // 新建凭证
36 | transportCreds := credentials.NewTLS(&tls.Config{
37 | ServerName: "server.razeen.me",
38 | Certificates: []tls.Certificate{cert},
39 | RootCAs: certPool,
40 | })
41 |
42 | dialOpt := grpc.WithTransportCredentials(transportCreds)
43 |
44 | conn, err := grpc.Dial("localhost:8080", dialOpt)
45 | if err != nil {
46 | log.Fatalf("Dial failed:%v", err)
47 | }
48 | defer conn.Close()
49 |
50 | client := pb.NewHelloWorldServiceClient(conn)
51 | resp1, err := client.SayHelloWorld(context.Background(), &pb.HelloWorldRequest{
52 | Greeting: "Hello Server 1 !!",
53 | Infos: map[string]string{"hello": "world"},
54 | })
55 | if err != nil {
56 | log.Fatal(err)
57 | }
58 | log.Printf("Resp1:%+v", resp1)
59 |
60 | resp2, err := client.SayHelloWorld(context.Background(), &pb.HelloWorldRequest{
61 | Greeting: "Hello Server 2 !!",
62 | })
63 | if err != nil {
64 | log.Fatalf("%v", err)
65 | }
66 | log.Printf("Resp2:%+v", resp2)
67 |
68 | // 服务器端流式 RPC;
69 | recvListHello, err := client.ListHello(context.Background(), &pb.HelloWorldRequest{Greeting: "Hello Server List Hello"})
70 | if err != nil {
71 | log.Fatalf("ListHello err: %v", err)
72 | }
73 |
74 | for {
75 | resp, err := recvListHello.Recv()
76 | if err == io.EOF {
77 | break
78 | }
79 | if err != nil {
80 | log.Fatal(err)
81 | }
82 |
83 | log.Printf("ListHello Server Resp: %v", resp.Reply)
84 | }
85 |
86 | // 客户端流式 RPC;
87 | sayMoreClient, err := client.SayMoreHello(context.Background())
88 | if err != nil {
89 | log.Fatal(err)
90 | }
91 | for i := 0; i < 3; i++ {
92 | sayMoreClient.Send(&pb.HelloWorldRequest{Greeting: fmt.Sprintf("SayMoreHello Hello Server %d", i)})
93 | }
94 |
95 | sayMoreResp, err := sayMoreClient.CloseAndRecv()
96 | if err != nil {
97 | log.Fatal(err)
98 | }
99 | log.Printf("SayMoreHello Server Resp: %v", sayMoreResp.Reply)
100 |
101 | // 双向流式 RPC;
102 | sayHelloChat, err := client.SayHelloChat(context.Background())
103 | if err != nil {
104 | log.Fatal(err)
105 | }
106 |
107 | go func() {
108 | for i := 0; i < 3; i++ {
109 | sayHelloChat.Send(&pb.HelloWorldRequest{Greeting: fmt.Sprintf("SayHelloChat Hello Server %d", i)})
110 | }
111 | }()
112 |
113 | for {
114 | resp, err := sayHelloChat.Recv()
115 | if err == io.EOF {
116 | break
117 | }
118 | if err != nil {
119 | log.Fatal(err)
120 | }
121 |
122 | log.Printf("SayHelloChat Server Say: %v", resp.Greeting)
123 | }
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/gin/README.md:
--------------------------------------------------------------------------------
1 | # [gin文件上传与下载]()
2 |
3 | Gin是用Go编写的web框架。性能还不错,而且使用比较简单,还支持RESTful API。
4 |
5 | 日常的使用中我们可能要处理一些文件的上传与下载,我这里简单总结一下。
6 |
7 |
8 | ### 单文件上传
9 |
10 | 我们使用`multipart/form-data`格式上传文件,利用`c.Request.FormFile`解析文件。
11 |
12 | ``` golang
13 | // HandleUploadFile 上传单个文件
14 | func HandleUploadFile(c *gin.Context) {
15 | file, header, err := c.Request.FormFile("file")
16 | if err != nil {
17 | c.JSON(http.StatusBadRequest, gin.H{"msg": "文件上传失败"})
18 | return
19 | }
20 |
21 | content, err := ioutil.ReadAll(file)
22 | if err != nil {
23 | c.JSON(http.StatusBadRequest, gin.H{"msg": "文件读取失败"})
24 | return
25 | }
26 |
27 | fmt.Println(header.Filename)
28 | fmt.Println(string(content))
29 | c.JSON(http.StatusOK, gin.H{"msg": "上传成功"})
30 | }
31 | ```
32 |
33 | 我们上传文件可以看到。
34 |
35 | 
36 |
37 | 我们已经看到文件上传成功,已经文件名字与内容。
38 |
39 |
40 | ### 多文件上传
41 |
42 | 多文件的上传利用`c.Request.MultipartForm`解析。
43 |
44 | ``` golang
45 | // HandleUploadMutiFile 上传多个文件
46 | func HandleUploadMutiFile(c *gin.Context) {
47 | // 限制放入内存的文件大小
48 | err := c.Request.ParseMultipartForm(4 << 20)
49 | if err != nil {
50 | c.JSON(http.StatusBadRequest, gin.H{"msg": "文件太大"})
51 | return
52 | }
53 | formdata := c.Request.MultipartForm
54 | files := formdata.File["file"]
55 |
56 | for _, v := range files {
57 | file, err := v.Open()
58 | if err != nil {
59 | c.JSON(http.StatusBadRequest, gin.H{"msg": "文件读取失败"})
60 | return
61 | }
62 | defer file.Close()
63 |
64 | content, err := ioutil.ReadAll(file)
65 | if err != nil {
66 | c.JSON(http.StatusBadRequest, gin.H{"msg": "文件读取失败"})
67 | return
68 | }
69 |
70 | fmt.Println(v.Filename)
71 | fmt.Println(string(content))
72 | }
73 |
74 | c.JSON(http.StatusOK, gin.H{"msg": "上传成功"})
75 | }
76 | ```
77 |
78 | 多个文件,遍历文件内容即可读取。
79 |
80 | ~~利用`c.Request.ParseMultipartForm()`可设置上传文件的大小,这里限制了4MB。~~
81 | `c.Request.ParseMultipartForm()`并不能限制上传文件的大小,只是限制了上传的文件读取到内存部分的大小,如果超过了就存入了系统的临时文件中。
82 | 如果需要限制文件大小,需要使用`github.com/gin-contrib/size`中间件,如demo中使用`r.Use(limits.RequestSizeLimiter(4 << 20))`限制最大4Mb。
83 |
84 | 我们看到
85 |
86 | 
87 |
88 | 两个文件已经上传成功。
89 |
90 |
91 | ### 文件下载
92 |
93 | 文件的下载主要是注意设置文件名,文件类型等。
94 |
95 | ``` golang
96 | // HandleDownloadFile 下载文件
97 | func HandleDownloadFile(c *gin.Context) {
98 | content := c.Query("content")
99 |
100 | content = "hello world, 我是一个文件," + content
101 |
102 | c.Writer.WriteHeader(http.StatusOK)
103 | c.Header("Content-Disposition", "attachment; filename=hello.txt")
104 | c.Header("Content-Type", "application/text/plain")
105 | c.Header("Accept-Length", fmt.Sprintf("%d", len(content)))
106 | c.Writer.Write([]byte(content))
107 | }
108 | ```
109 |
110 | 通过
111 | - `Content-Disposition`设置文件名字;
112 | - `Content-Type`设置文件类型,可以到[这里](http://www.runoob.com/http/http-content-type.html)查阅;
113 | - `Accept-Length`这个设置文件长度;
114 | - `c.Writer.Write`写出文件。
115 |
116 | 成功下载可以看到:
117 |
118 | 
119 |
120 |
121 | * 完整demo[在这里](https://github.com/razeencheng/demo-go/blob/master/gin/gin.go)
122 |
--------------------------------------------------------------------------------
/timer/README.md:
--------------------------------------------------------------------------------
1 | # [Go学习笔记(九) 计时器的生命周期[译]](https://razeencheng.com/post/go-timers-life-cycle.html)
2 |
3 |
4 |
5 | 
6 |
7 |
8 |
9 | *全文基于GO 1.14*
10 |
11 |
12 |
13 | 计时器在定时执行一些任务时很有用。Go内部依靠调度器来管理创建的计时器。而Go的调度程序是协作式的调度方式,这会让整个调度看起来比较复杂,因为goroutune必须自己停止(依赖channel阻塞或system call), 或者由调度器自己在某个调度点暂停。
14 |
15 |
16 |
17 |
18 |
19 | *有关抢占的更多信息,建议您阅读作者的文章[Go: Goroutine and Preemption](https://medium.com/a-journey-with-go/go-goroutine-and-preemption-d6bc2aa2f4b7)*.
20 |
21 |
22 |
23 | ### 生命周期
24 |
25 | 下面是一段简单示例代码:
26 |
27 | ```go
28 | func main(){
29 | sigs := make(chan os.Signal,1)
30 | signal.Notify(sigs,syscall.SIGINT,syscall.SIGTERM)
31 |
32 | time.AfterFunc(time.Second, func() {
33 | println("done")
34 | })
35 |
36 | <- sigs
37 | }
38 | ```
39 |
40 | 计时器创建后,他会保存到一个链接到当前P的计时器内部列表上,下图就是这段代码的表示形式:
41 |
42 | 
43 |
44 | *有关G,M,P模型的更多信息,建议您阅读作者的文章[Go: Goroutine, OS Thread and CPU Management](https://medium.com/a-journey-with-go/go-goroutine-os-thread-and-cpu-management-2f5a5eaf518a)*
45 |
46 |
47 |
48 |
49 |
50 | 从图中可以看到,一旦创建了计时器,它就会注册一个内部回调,该内部回调将使用`go`回调用户函数,并将其转换为goroutine。
51 |
52 |
53 |
54 | 然后,将通过调度程序管理计时器。在每一轮调度中,它都会检查计时器是否准备好运行,如果准备就绪,则准备运行。实际上,由于Go调度程序本身不会运行任何代码,因此运行计时器的回调会将其goroutine排队到本地队列中。然后,当调度程序在队列中将其接收时,goroutine将运行。如选图所示:
55 |
56 | 
57 |
58 | 根据本地队列的大小,计时器运行可能会稍有延迟。不过,由于Go 1.14中的异步抢占,goroutines在运行时间10ms后会被抢占,降低了延迟的可能性。
59 |
60 |
61 |
62 | ### 延迟?
63 |
64 | 为了了解计时器可能存在的延迟,我们创造一个场景:从同一goroutine创建大量计时器。
65 |
66 | 由于计时器都链接到当前`P`,因此繁忙的`P`无法及时运行其链接的计时器。代码如下:
67 |
68 | ``` go
69 | func main(){
70 |
71 | trace.Start(os.Stderr)
72 | defer trace.Stop()
73 |
74 | sigs := make(chan os.Signal,1)
75 | signal.Notify(sigs,syscall.SIGINT,syscall.SIGTERM)
76 |
77 | //time.AfterFunc(time.Second, func() {
78 | // println("done")
79 | //})
80 |
81 |
82 | var num int64
83 |
84 | for i:=0; i< 1e3 ; i++ {
85 | time.AfterFunc(time.Second, func() {
86 | atomic.AddInt64(&num,1)
87 | })
88 | }
89 |
90 | t:= 0
91 | for i:=0;i<1e10; i++ {
92 | t ++
93 | }
94 | _ = t
95 |
96 | <- sigs
97 |
98 | println(num,"timers created,",t,"iterations done")
99 | }
100 | ```
101 |
102 | 通过go tool trace, 我们可以看到goroutine正在占用处理器:
103 |
104 | 
105 |
106 | 由于异步抢占的原因,代表正在运行的goroutine图形被分成了大量较小的块。
107 |
108 |
109 |
110 | 在这些块中,一个空间看起来比其他空间大。让我们放大一下:
111 |
112 | 
113 |
114 |
115 |
116 | 在该计时器需要运行时,就会发生改情况。此时,当前goroutine已被Go调度程序抢占和取代。调度程序将计时器转换为可运行的goroutine,如图所示。
117 |
118 | 但是,当前线程的Go调度程序并不是唯一运行计时器的调度程序。Go实施了一种计时器窃取策略,以确保在当前P繁忙时可以由另一个P运行计时器。由于异步抢占,它不太可能发生,但是在我们的示例中,由于使用了大量的计时器,它发生了。如下图所示:
119 |
120 | 
121 |
122 |
123 |
124 | 如果我们不考虑计时器窃取,将发生以下情况:
125 |
126 | 
127 |
128 |
129 |
130 | 持有计时器的所有goroutine都会添加到本地队列中。然后,由于 `P`之间的窃取,将准确的调度计时器。
131 |
132 | 所以,由于异步抢占和工作窃取,延迟几乎不可能发生。
133 |
134 |
135 |
136 | > 原文 [Go: Timers’ Life Cycle](https://medium.com/a-journey-with-go/go-timers-life-cycle-403f3580093a)
--------------------------------------------------------------------------------
/oauth2/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "html/template"
7 | "log"
8 | "net/http"
9 | "os"
10 | )
11 |
12 | // 你在注册时得到的
13 | const (
14 | clientID = "你的客户端ID"
15 | clientSecret = "你的客户端密钥"
16 | )
17 |
18 | var httpClient = http.Client{}
19 |
20 | type OAuthAccessResponse struct {
21 | AccessToken string `json:"access_token"`
22 | }
23 |
24 | type GithubUserInfo struct {
25 | Name string `json:"name"`
26 | }
27 |
28 | func main() {
29 | fs := http.FileServer(http.Dir("public"))
30 | http.Handle("/", fs)
31 | http.HandleFunc("/oauth/redirect", HandleOAuthRedirect)
32 |
33 | http.ListenAndServe(":8080", nil)
34 | }
35 |
36 | // HandleOAuthRedirect doc
37 | func HandleOAuthRedirect(w http.ResponseWriter, r *http.Request) {
38 | // 首先,我们从URI中解析出code参数
39 | // 如: http://localhost:8080/oauth/redirect?code=260f17a7308f2c566725
40 | err := r.ParseForm()
41 | if err != nil {
42 | log.Printf("could not parse query: %v", err)
43 | w.WriteHeader(http.StatusBadRequest)
44 | }
45 | code := r.FormValue("code")
46 |
47 | // 通过github返回的code码,再向github获取access token,只有使用access token才能获取用户资源
48 | accessToken, err := getAccessTokenByCode(code)
49 | if err != nil {
50 | w.WriteHeader(http.StatusInternalServerError)
51 | return
52 | }
53 |
54 | // 通过access token获取用户资源,这里的资源为用户的名称
55 | username, err := getUsername(accessToken)
56 | if err != nil {
57 | w.WriteHeader(http.StatusInternalServerError)
58 | return
59 | }
60 |
61 | // 最后获取到用户信息后,我们重定向到欢迎页面,也就是表示用户登录成功
62 | w.Header().Set("Location", "/welcome.html?username="+username)
63 | w.WriteHeader(http.StatusFound)
64 | }
65 |
66 | func getAccessTokenByCode(code string) (string, error) {
67 | reqURL := fmt.Sprintf("https://github.com/login/oauth/access_token?client_id=%s&client_secret=%s&code=%s",
68 | clientID, clientSecret, code)
69 | req, err := http.NewRequest(http.MethodPost, reqURL, nil)
70 | if err != nil {
71 | log.Printf("could not create HTTP request: %v", err)
72 | return "", err
73 | }
74 |
75 | // 设置我们期待返回的格式为json
76 | req.Header.Set("accept", "application/json")
77 |
78 | // 发送http请求
79 | res, err := httpClient.Do(req)
80 | if err != nil {
81 | log.Printf("could not send HTTP request: %v", err)
82 | return "", err
83 | }
84 | defer res.Body.Close()
85 |
86 | // 解析
87 | var t OAuthAccessResponse
88 | if err := json.NewDecoder(res.Body).Decode(&t); err != nil {
89 | log.Printf("could not parse JSON response: %v", err)
90 | return "", err
91 | }
92 |
93 | return t.AccessToken, nil
94 | }
95 |
96 | func getUsername(accessToken string) (string, error) {
97 | req, err := http.NewRequest(http.MethodGet, "https://api.github.com/user", nil)
98 | if err != nil {
99 | log.Printf("could not create HTTP request: %v", err)
100 | return "", err
101 | }
102 |
103 | // 设置我们期待返回的格式为json
104 | req.Header.Set("accept", "application/json")
105 | req.Header.Set("Authorization", "Bearer "+accessToken)
106 |
107 | // 发送http请求
108 | res, err := httpClient.Do(req)
109 | if err != nil {
110 | log.Printf("could not send HTTP request: %v", err)
111 | return "", err
112 | }
113 | defer res.Body.Close()
114 |
115 | // 解析
116 | var u GithubUserInfo
117 | if err := json.NewDecoder(res.Body).Decode(&u); err != nil {
118 | log.Printf("could not parse JSON response: %v", err)
119 | return "", err
120 | }
121 |
122 | return u.Name, nil
123 | }
124 |
125 | func init() {
126 | tmpl, err := template.ParseFiles("public/index.tmpl")
127 | if err != nil {
128 | log.Fatalf("parse html templ err: %v", err)
129 | }
130 |
131 | file, err := os.OpenFile("public/index.html", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0660)
132 | if err != nil {
133 | log.Fatalf("create index.html err: %v", err)
134 | }
135 | defer file.Close()
136 |
137 | err = tmpl.Execute(file, map[string]interface{}{
138 | "ClientId": clientID,
139 | })
140 | if err != nil {
141 | log.Fatalf("exec tmpl err: %v", err)
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 学习golang的一些Demo
2 |
3 | *更多内容请看博客 👉 https://razeencheng.com*
4 |
5 | ### Demo 一览
6 |
7 | 1. [Go学习笔记(二) | 我对recover的一点误解](https://razeen.me/post/daily-go-recover.html) 👉 [recover](https://github.com/razeencheng/demo-go/tree/master/recover)
8 | 2. [Go学习笔记(三) | 怎么写Go基准测试(性能测试)](https://razeen.me/post/go-how-to-write-benchmark.html) 👉 [benchmark](https://github.com/razeencheng/demo-go/tree/master/benchmark)
9 | 3. [gRPC在Go中的使用(一)Protocol Buffers语法与相关使用](https://razeen.me/post/how-to-use-grpc-in-golang-01.html) 👉 [grpc/demo1](https://github.com/razeencheng/demo-go/tree/master/grpc/demo1)
10 | 4. [gRPC在Go中的使用(二)gRPC实现简单通讯](https://razeen.me/post/how-to-use-grpc-in-golang-02.html) 👉 [grpc/demo2](https://github.com/razeencheng/demo-go/tree/master/grpc/demo2)
11 | 5. [gRPC在Go中的使用(三)gRPC实现TLS加密通信与流模式](https://razeen.me/post/how-to-use-grpc-in-golang-03.html) 👉 [grpc/demo3](https://github.com/razeencheng/demo-go/tree/master/grpc/demo3)
12 |
13 | 6. json tag 使用 👉 [json/tag](https://github.com/razeencheng/demo-go/tree/master/json/tag)
14 |
15 | 7. [gin文件上传与下载](https://newb.razeen.me/gin-file-download-and-upload/) 👉 [gin/gin.go](https://github.com/razeencheng/demo-go/blob/master/gin/gin.go)
16 |
17 | 8. [Go学习笔记(六) 使用swaggo自动生成Restful API文档](https://razeen.me/post/go-swagger.html) 👉 [swaggo-gin](https://github.com/razeencheng/demo-go/tree/master/swaggo-gin)
18 |
19 | 9. [Go学习笔记(七) | 理解并实现 OAuth 2.0](https://razeen.me/post/oauth2-protocol-details.html) 👉 [oauth2](https://github.com/razeencheng/demo-go/tree/master/oauth2)
20 |
21 | 10. [如何用Go调用Windows API](https://razeencheng.com/post/breaking-all-the-rules-using-go-to-call-windows-api.html) 👉 [windows_api](https://github.com/razeencheng/demo-go/tree/master/windows_api)
22 |
23 | 11. [Go学习笔记(八) | 使用 os/exec 执行命令](https://razeencheng.com/post/simple-use-go-exec-command.html) 👉 [os-exec](https://github.com/razeencheng/demo-go/tree/master/os-exec)
24 |
25 | 12. [Golang中的RESTful API最佳实践](https://razeencheng.com/post/golang-and-restful-api.html) 👉[restful-api](https://github.com/razeencheng/demo-go/tree/master/restful-api)
26 |
27 | 13. [Go学习笔记(九) 计时器的生命周期[译]](https://razeencheng.com/post/go-timers-life-cycle.html) 👉 [timer](https://github.com/razeencheng/demo-go/tree/master/timer)
28 |
29 | 14. [Go学习笔记(十)老项目迁移 go module 大型灾难记录](https://razeencheng.com/post/accidents-of-migrating-to-go-modules.html) 👉 [gomod](https://github.com/razeencheng/demo-go/tree/master/gomod)
30 |
31 | ### 目录
32 |
33 | ````
34 | .
35 | ├── README.md
36 | ├── benchmark
37 | │ ├── main.go
38 | │ └── main_test.go
39 | ├── gin
40 | │ ├── gin.go
41 | ├── grpc
42 | │ ├── demo1
43 | │ │ └── helloworld
44 | │ │ ├── hello_world.pb.go
45 | │ │ └── hello_world.proto
46 | │ ├── demo2
47 | │ │ ├── client
48 | │ │ │ └── client.go
49 | │ │ ├── helloworld
50 | │ │ │ ├── hello_world.pb.go
51 | │ │ │ └── hello_world.proto
52 | │ │ └── server
53 | │ │ └── server.go
54 | │ └── demo3
55 | │ ├── client
56 | │ │ ├── certs
57 | │ │ └── client.go
58 | │ ├── helloworld
59 | │ │ ├── hello_world.pb.go
60 | │ │ └── hello_world.proto
61 | │ └── server
62 | │ ├── certs
63 | │ └── server.go
64 | ├── json
65 | │ └── tag
66 | │ └── tag.go
67 | │── recover
68 | │ └── recover.go
69 | ├── oauth2
70 | │ ├── README.md
71 | │ ├── main.go
72 | │ └── public
73 | │ ├── index.html
74 | │ └── welcome.html
75 | ├── os-exec
76 | │ ├── README.md
77 | │ ├── main.go
78 | │ └── testcmd
79 | │ ├── main.go
80 | │ └── testcmd
81 | ├── recover
82 | │ ├── README.md
83 | │ └── recover.go
84 | ├── swaggo-gin
85 | │ ├── Makefile
86 | │ ├── README.md
87 | │ ├── doc.go
88 | │ ├── docs
89 | │ │ ├── docs.go
90 | │ │ └── swagger
91 | │ │ ├── swagger.json
92 | │ │ └── swagger.yaml
93 | │ ├── handle.go
94 | │ └── main.go
95 | │
96 | └── windows_api
97 | ├── README.md
98 | └── main_windows.go
99 |
100 | ````
101 |
--------------------------------------------------------------------------------
/grpc/demo3/server/server.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "crypto/tls"
6 | "crypto/x509"
7 | "io"
8 | "io/ioutil"
9 | "log"
10 | "net"
11 | "time"
12 |
13 | "google.golang.org/grpc/credentials"
14 |
15 | "github.com/golang/protobuf/ptypes"
16 | "github.com/golang/protobuf/ptypes/any"
17 | "google.golang.org/grpc"
18 |
19 | pb "github.com/razeencheng/demo-go/grpc/demo3/helloworld"
20 | )
21 |
22 | type SayHelloServer struct{}
23 |
24 | // 服务器端流式 RPC, 接收一次客户端请求,返回一个流
25 | func (s *SayHelloServer) ListHello(in *pb.HelloWorldRequest, stream pb.HelloWorldService_ListHelloServer) error {
26 | log.Printf("Client Say: %v", in.Greeting)
27 |
28 | stream.Send(&pb.HelloWorldResponse{Reply: "ListHello Reply " + in.Greeting + " 1"})
29 | time.Sleep(1 * time.Second)
30 | stream.Send(&pb.HelloWorldResponse{Reply: "ListHello Reply " + in.Greeting + " 2"})
31 | time.Sleep(1 * time.Second)
32 | stream.Send(&pb.HelloWorldResponse{Reply: "ListHello Reply " + in.Greeting + " 3"})
33 | time.Sleep(1 * time.Second)
34 | return nil
35 | }
36 |
37 | // 客户端流式 RPC, 客户端流式请求,服务器可返回一次
38 | func (s *SayHelloServer) SayMoreHello(stream pb.HelloWorldService_SayMoreHelloServer) error {
39 | // 接受客户端请求
40 | for {
41 | req, err := stream.Recv()
42 | if err == io.EOF {
43 | break
44 | }
45 |
46 | if err != nil {
47 | return err
48 | }
49 |
50 | log.Printf("SayMoreHello Client Say: %v", req.Greeting)
51 | }
52 |
53 | // 流读取完成后,返回
54 | return stream.SendAndClose(&pb.HelloWorldResponse{Reply: "SayMoreHello Recv Muti Greeting"})
55 | }
56 |
57 | func (s *SayHelloServer) SayHelloChat(stream pb.HelloWorldService_SayHelloChatServer) error {
58 |
59 | go func() {
60 | for {
61 | req, err := stream.Recv()
62 | if err == io.EOF {
63 | break
64 | }
65 |
66 | if err != nil {
67 | return
68 | }
69 |
70 | log.Printf("SayHelloChat Client Say: %v", req.Greeting)
71 | }
72 | }()
73 |
74 | stream.Send(&pb.HelloWorldRequest{Greeting: "SayHelloChat Server Say Hello 1"})
75 | time.Sleep(1 * time.Second)
76 | stream.Send(&pb.HelloWorldRequest{Greeting: "SayHelloChat Server Say Hello 2"})
77 | time.Sleep(1 * time.Second)
78 | stream.Send(&pb.HelloWorldRequest{Greeting: "SayHelloChat Server Say Hello 3"})
79 | time.Sleep(1 * time.Second)
80 | return nil
81 | }
82 |
83 | func (s *SayHelloServer) SayHelloWorld(ctx context.Context, in *pb.HelloWorldRequest) (res *pb.HelloWorldResponse, err error) {
84 | log.Printf("Client Greeting:%s", in.Greeting)
85 | log.Printf("Client Info:%v", in.Infos)
86 |
87 | var an *any.Any
88 | if in.Infos["hello"] == "world" {
89 | an, err = ptypes.MarshalAny(&pb.HelloWorld{Msg: "Good Request"})
90 | } else {
91 | an, err = ptypes.MarshalAny(&pb.Error{Msg: []string{"Bad Request", "Wrong Info Msg"}})
92 | }
93 |
94 | if err != nil {
95 | return
96 | }
97 | return &pb.HelloWorldResponse{
98 | Reply: "Hello World !!",
99 | Details: []*any.Any{an},
100 | }, nil
101 | }
102 |
103 | func main() {
104 | lis, err := net.Listen("tcp", ":8080")
105 | if err != nil {
106 | panic(err)
107 | }
108 |
109 | // 加载证书和密钥 (同时能验证证书与私钥是否匹配)
110 | cert, err := tls.LoadX509KeyPair("certs/test_server.pem", "certs/test_server.key")
111 | if err != nil {
112 | panic(err)
113 | }
114 |
115 | // 将根证书加入证书词
116 | // 测试证书的根如果不加入可信池,那么测试证书将视为不可惜,无法通过验证。
117 | certPool := x509.NewCertPool()
118 | rootBuf, err := ioutil.ReadFile("certs/root.pem")
119 | if err != nil {
120 | panic(err)
121 | }
122 |
123 | if !certPool.AppendCertsFromPEM(rootBuf) {
124 | panic("fail to append test ca")
125 | }
126 |
127 | tlsConf := &tls.Config{
128 | ClientAuth: tls.RequireAndVerifyClientCert,
129 | Certificates: []tls.Certificate{cert},
130 | ClientCAs: certPool,
131 | }
132 |
133 | serverOpt := grpc.Creds(credentials.NewTLS(tlsConf))
134 | grpcServer := grpc.NewServer(serverOpt)
135 |
136 | pb.RegisterHelloWorldServiceServer(grpcServer, &SayHelloServer{})
137 |
138 | log.Println("Server Start...")
139 | grpcServer.Serve(lis)
140 | }
141 |
--------------------------------------------------------------------------------
/gin/gin.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "io/ioutil"
6 | "net/http"
7 |
8 | limits "github.com/gin-contrib/size"
9 | "github.com/gin-gonic/contrib/sessions"
10 | "github.com/gin-gonic/gin"
11 | )
12 |
13 | // some
14 | const (
15 | USER = "admin"
16 | PWD = "admin"
17 | )
18 |
19 | func main() {
20 | r := gin.Default()
21 |
22 | store := sessions.NewCookieStore([]byte("jdagldagsdadhsbdgaj"))
23 | store.Options(sessions.Options{
24 | MaxAge: 7200,
25 | Path: "/",
26 | Secure: true,
27 | HttpOnly: true,
28 | })
29 | // 限制文件大小
30 | r.Use(limits.RequestSizeLimiter(4 << 20))
31 | r.Use(sessions.Sessions("httpsgateway", store))
32 | r.NoRoute(func(c *gin.Context) { c.JSON(http.StatusNotFound, "Invaild api request") })
33 |
34 | r.POST("login", HandlleLogin)
35 | api := r.Group("api", Auth())
36 | {
37 | api.GET("logout", HandleLogout)
38 | api.GET("hello_world", HandleHelloWorld)
39 | }
40 |
41 | t := r.Group("test")
42 | {
43 | t.POST("upload_file", HandleUploadFile)
44 | t.POST("upload_muti_file", HandleUploadMutiFile)
45 | t.GET("download", HandleDownloadFile)
46 | }
47 |
48 | // r.RunTLS(":8443", "./cert.pem", "./key.pem")
49 | r.Run(":8888")
50 | }
51 |
52 | // Auth doc
53 | func Auth() gin.HandlerFunc {
54 | return func(c *gin.Context) {
55 | session := sessions.Default(c)
56 | u := session.Get("user")
57 | if u == nil {
58 | c.JSON(http.StatusUnauthorized, gin.H{"msg": "您暂未登录"})
59 | c.Abort()
60 | return
61 | }
62 | }
63 | }
64 |
65 | // HandlleLogin doc
66 | func HandlleLogin(c *gin.Context) {
67 | user := c.PostForm("user")
68 | password := c.PostForm("password")
69 |
70 | if user != USER || password != PWD {
71 | c.JSON(http.StatusBadRequest, gin.H{"msg": "用户名或密码不正确"})
72 | return
73 | }
74 |
75 | session := sessions.Default(c)
76 | session.Set("user", USER)
77 | session.Save()
78 | c.JSON(http.StatusOK, gin.H{"msg": "login succeed"})
79 |
80 | }
81 |
82 | // HandleLogout doc
83 | func HandleLogout(c *gin.Context) {
84 | session := sessions.Default(c)
85 | session.Delete("user")
86 | session.Save()
87 | c.JSON(http.StatusOK, gin.H{"data": "See you!"})
88 | }
89 |
90 | // HandleHelloWorld doc
91 | func HandleHelloWorld(c *gin.Context) {
92 | c.JSON(http.StatusOK, gin.H{"data": "Hello World!"})
93 | }
94 |
95 | // HandleUploadFile 上传单个文件
96 | func HandleUploadFile(c *gin.Context) {
97 | file, header, err := c.Request.FormFile("file")
98 | if err != nil {
99 | c.JSON(http.StatusBadRequest, gin.H{"msg": "文件上传失败"})
100 | return
101 | }
102 |
103 | content, err := ioutil.ReadAll(file)
104 | if err != nil {
105 | c.JSON(http.StatusBadRequest, gin.H{"msg": "文件读取失败"})
106 | return
107 | }
108 |
109 | fmt.Println(header.Filename)
110 | fmt.Println(string(content))
111 | c.JSON(http.StatusOK, gin.H{"msg": "上传成功"})
112 | }
113 |
114 | // HandleUploadMutiFile 上传多个文件
115 | func HandleUploadMutiFile(c *gin.Context) {
116 |
117 | // 限制上传文件大小
118 | c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 4<<20)
119 |
120 | // 限制放入内存的文件大小
121 | err := c.Request.ParseMultipartForm(4 << 20)
122 | if err != nil {
123 | c.JSON(http.StatusBadRequest, gin.H{"msg": "文件读取失败"})
124 | return
125 | }
126 | formdata := c.Request.MultipartForm
127 | files := formdata.File["file"]
128 |
129 | for _, v := range files {
130 | file, err := v.Open()
131 | if err != nil {
132 | c.JSON(http.StatusBadRequest, gin.H{"msg": "文件读取失败"})
133 | return
134 | }
135 | defer file.Close()
136 |
137 | content, err := ioutil.ReadAll(file)
138 | if err != nil {
139 | c.JSON(http.StatusBadRequest, gin.H{"msg": "文件读取失败"})
140 | return
141 | }
142 |
143 | fmt.Println(v.Filename)
144 | fmt.Println(string(content))
145 | }
146 |
147 | c.JSON(http.StatusOK, gin.H{"msg": "上传成功"})
148 | }
149 |
150 | // HandleDownloadFile 下载文件
151 | func HandleDownloadFile(c *gin.Context) {
152 | content := c.Query("content")
153 |
154 | content = "hello world, 我是一个文件," + content
155 |
156 | c.Writer.WriteHeader(http.StatusOK)
157 | c.Header("Content-Disposition", "attachment; filename=hello.txt")
158 | c.Header("Content-Type", "application/text/plain")
159 | c.Header("Accept-Length", fmt.Sprintf("%d", len(content)))
160 | c.Writer.Write([]byte(content))
161 | }
162 |
--------------------------------------------------------------------------------
/grpc/demo2/README.md:
--------------------------------------------------------------------------------
1 | # [gRPC在Go中的使用(二)gRPC实现简单通讯](https://razeen.me/post/how-to-use-grpc-in-golang-02.html)
2 |
3 | Desc:gRPC实现简单通讯,Google 开源 RPC 框架 gRPC 初探
4 |
5 |
6 |
7 | 在上一篇中,我们用protobuf定义了两个消息`HelloWorldRequest`与`HelloWorldResponse`以及一个`HelloWorldService`服务。同时,我们还生成了相应的go代码`.pb.go`。
8 |
9 | 那么客户端与服务端怎么去通过这些接口去完成通讯呢?下面我们一起实现一个简单的gRPC通讯。
10 |
11 |
12 |
13 |
14 |
15 | 在RPC通讯中,客户端使用存根(SayHelloWorld)发送请求到服务器并且等待响应返回,整个过程就像我们平常函数调用一样。
16 |
17 | ```rpc
18 | service HelloWorldService {
19 | rpc SayHelloWorld(HelloWorldRequest) returns (HelloWorldResponse){}
20 | }
21 | ```
22 |
23 | 那么接下来,我们先创建一个服务端。
24 |
25 |
26 |
27 | ### 创建服务端
28 |
29 |
30 | 在生成的`hello_world.pb.go`中,已经为我们生成了服务端的接口:
31 |
32 | ```go
33 | // HelloWorldServiceServer is the server API for HelloWorldService service.
34 | type HelloWorldServiceServer interface {
35 | SayHelloWorld(context.Context, *HelloWorldRequest) (*HelloWorldResponse, error)
36 | }
37 | ```
38 |
39 | 在服务端我们首先要做的就是实现这个接口。
40 |
41 | ```go
42 | package main
43 |
44 | import (
45 | "context"
46 | "log"
47 | "net"
48 |
49 | pb "github.com/razeencheng/demo-go/grpc/demo2/helloworld"
50 |
51 | "github.com/golang/protobuf/ptypes"
52 | "github.com/golang/protobuf/ptypes/any"
53 | "google.golang.org/grpc"
54 | )
55 |
56 | type SayHelloServer struct{}
57 |
58 | func (s *SayHelloServer) SayHelloWorld(ctx context.Context, in *pb.HelloWorldRequest) (res *pb.HelloWorldResponse, err error) {
59 | log.Printf("Client Greeting:%s", in.Greeting)
60 | log.Printf("Client Info:%v", in.Infos)
61 |
62 | var an *any.Any
63 | if in.Infos["hello"] == "world" {
64 | an, err = ptypes.MarshalAny(&pb.HelloWorld{Msg: "Good Request"})
65 | } else {
66 | an, err = ptypes.MarshalAny(&pb.Error{Msg: []string{"Bad Request", "Wrong Info Msg"}})
67 | }
68 |
69 | if err != nil {
70 | return
71 | }
72 | return &pb.HelloWorldResponse{
73 | Reply: "Hello World !!",
74 | Details: []*any.Any{an},
75 | }, nil
76 | }
77 | ```
78 |
79 | 简单如上面的几行,实现了这个接口我们只需要创建一个结构`SayHelloServer`,同时实现`HelloWorldServiceServer`的所有方法即可。
80 |
81 | 这里为了演示效果我打印了一些数据,同时利用`any.Any`在不同的情况下返回不同的类型数据。
82 |
83 |
84 |
85 | 当然,只是现实了接口还不够,我们还需要启动一个服务,这样客户端才能使用该服务。启动服务很简单,就像我们平常启用一个Server一样。
86 |
87 | ```go
88 | func main() {
89 | // 我们首先须监听一个tcp端口
90 | lis, err := net.Listen("tcp", ":8080")
91 | if err != nil {
92 | panic(err)
93 | }
94 |
95 | // 新建一个grpc服务器
96 | grpcServer := grpc.NewServer()
97 | // 向grpc服务器注册SayHelloServer
98 | pb.RegisterHelloWorldServiceServer(grpcServer, &SayHelloServer{})
99 | // 启动服务
100 | grpcServer.Serve(lis)
101 | }
102 | ```
103 |
104 | 从上面的代码,我们可以看到,简单的4步即可启动一个服务。
105 |
106 | 1. 监听一个服务端口,供客户端调用;
107 | 2. 创建一个grpc服务器,当然这里可以设置`授权认证`,这个在下一篇中我们将详细介绍;
108 | 3. 注册服务,其实是调用生存的`.pb.go`中的`RegisterHelloWorldServiceServer`方法,将我们这里实现的`SayHelloServer`加入到该服务中。
109 | 4. 启动服务,等待客户端连接。
110 |
111 | 我们` go run server.go`,无任何报错,这样一个简单的grpc服务的服务端就准备就绪了。接下来我们看看客户端。
112 |
113 |
114 |
115 | ### 创建客户端
116 |
117 |
118 | 例如:
119 |
120 | ```go
121 | package main
122 |
123 | import (
124 | "context"
125 | "log"
126 |
127 | "google.golang.org/grpc"
128 |
129 | pb "github.com/razeencheng/demo-go/grpc/demo2/helloworld"
130 | )
131 |
132 | func main() {
133 | // 创建一个 gRPC channel 和服务器交互
134 | conn, err := grpc.Dial("localhost:8080", grpc.WithInsecure())
135 | if err != nil {
136 | log.Fatalf("Dial failed:%v", err)
137 | }
138 | defer conn.Close()
139 |
140 | // 创建客户端
141 | client := pb.NewHelloWorldServiceClient(conn)
142 |
143 | // 直接调用
144 | resp1, err := client.SayHelloWorld(context.Background(), &pb.HelloWorldRequest{
145 | Greeting: "Hello Server 1 !!",
146 | Infos: map[string]string{"hello": "world"},
147 | })
148 |
149 | log.Printf("Resp1:%+v", resp1)
150 |
151 | resp2, err := client.SayHelloWorld(context.Background(), &pb.HelloWorldRequest{
152 | Greeting: "Hello Server 2 !!",
153 | })
154 |
155 | log.Printf("Resp2:%+v", resp2)
156 | }
157 | ```
158 |
159 | 客户端的实现比服务端更简洁,三步即可。
160 |
161 | 1. 创建一个 gRPC channel 和服务器交互。这里也是可以设置`授权认证`的;
162 | 2. 创建一个客户端去执行RPC。用到的也是`.pb.go`内的`NewHelloWorldServiceClient`方法;
163 | 3. 像函数调用一样去调用RPC服务。
164 |
165 |
166 |
167 | 我直接RUN起来,如下,我们可以看到客户端发送到服务的消息以及服务端对不同消息的不同回复。
168 |
169 | 
170 |
171 |
172 |
173 | 那么到这里,我们简单的实现了一个gRPC通讯。但很多时候,我们可能希望客户端与服务器能更安全的通信,或者客户端与服务器不再是一种固定的结构的传输,需要流式的去处理一些问题等等。针对这些问题,在下一篇博客中,我将结合实例详细说明。
174 |
175 |
176 |
177 | *文中完整代码在[这里](https://github.com/razeencheng/demo-go/tree/master/grpc/demo2)。*
--------------------------------------------------------------------------------
/windows_api/main_windows.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/hex"
5 | "fmt"
6 | "syscall"
7 | "unicode/utf16"
8 | "unsafe"
9 |
10 | "github.com/pkg/errors"
11 | )
12 |
13 | func main() {
14 | var order uint32 = 1 // True
15 | var ulAf uint32 = AF_INET
16 | var tableClass uint32 = 0
17 |
18 | buffer, err := GetExtendedTcpTable(order, ulAf, tableClass)
19 | if err != nil {
20 | fmt.Println(err)
21 | return
22 | }
23 |
24 | fmt.Println(hex.Dump(buffer))
25 |
26 | pTable := (*MIB_TCPTABLE_OWNER_PID)(unsafe.Pointer(&buffer[0]))
27 |
28 | rows := make([]MIB_TCPROW_OWNER_PID, int(pTable.dwNumEntries))
29 | for i := 0; i < int(pTable.dwNumEntries); i++ {
30 | rows[i] = *(*MIB_TCPROW_OWNER_PID)(unsafe.Pointer(
31 | uintptr(unsafe.Pointer(&pTable.table[0])) +
32 | uintptr(i)*unsafe.Sizeof(pTable.table[0])))
33 | }
34 | show(rows)
35 |
36 | rows2 := ((*[1 << 30]MIB_TCPROW_OWNER_PID)(unsafe.Pointer(&pTable.table[0]))[:int(pTable.dwNumEntries):int(pTable.dwNumEntries)])
37 | show(rows2)
38 |
39 | }
40 |
41 | var (
42 | kernel32DLL = syscall.NewLazyDLL("kernel32.dll")
43 | procCreateJobObjectW = kernel32DLL.NewProc("CreateJobObjectW")
44 |
45 | iphlpapiDLL = syscall.NewLazyDLL("iphlpapi.dll")
46 | procGetExtendedTcpTable = iphlpapiDLL.NewProc("GetExtendedTcpTable")
47 | )
48 |
49 | const (
50 | AF_INET = 2
51 | )
52 |
53 | type MIB_TCPTABLE_OWNER_PID struct {
54 | dwNumEntries uint32
55 | table [1]MIB_TCPROW_OWNER_PID
56 | }
57 |
58 | type MIB_TCPROW_OWNER_PID struct {
59 | dwState uint32
60 | dwLocalAddr [4]byte
61 | dwLocalPort uint32
62 | dwRemoteAddr [4]byte
63 | dwRemotePort uint32
64 | dwOwningPid uint32
65 | }
66 |
67 | func show(raws []MIB_TCPROW_OWNER_PID) {
68 | for _, v := range raws {
69 | v.show()
70 | }
71 | }
72 |
73 | func (m *MIB_TCPROW_OWNER_PID) show() {
74 | fmt.Printf(`
75 | state: %d
76 | loadAddr: %s
77 | localPort: %d
78 | remoteAddr: %s
79 | remotePort: %d
80 | pid: %d`,
81 | m.dwState,
82 | string(m.dwLocalAddr[:]),
83 | m.dwLocalPort,
84 | string(m.dwRemoteAddr[:]),
85 | m.dwRemotePort,
86 | m.dwOwningPid)
87 | }
88 |
89 | // GetExtendedTcpTable function retrieves a table that contains a list of TCP endpoints available to the application.
90 | func GetExtendedTcpTable(order, ulAf, tableClass uint32) ([]byte, error) {
91 |
92 | var dwSize uint32
93 | ret, _, err := procGetExtendedTcpTable.Call(
94 | 0, // PVOID
95 | uintptr(unsafe.Pointer(&dwSize)), // PDWORD
96 | uintptr(order), // BOOL
97 | uintptr(ulAf), // ULONG
98 | uintptr(tableClass), // TCP_TABLE_CLASS
99 | 0, // ULONG
100 | )
101 | if ret == 0 {
102 | return nil, errors.Wrapf(err, "get extended tcp table size failed code %x", ret)
103 | }
104 |
105 | if syscall.Errno(ret) == syscall.ERROR_INSUFFICIENT_BUFFER {
106 | buffer := make([]byte, int(dwSize))
107 |
108 | ret, _, err := procGetExtendedTcpTable.Call(
109 | uintptr(unsafe.Pointer(&buffer[0])),
110 | uintptr(unsafe.Pointer(&dwSize)),
111 | uintptr(order),
112 | uintptr(ulAf),
113 | uintptr(tableClass),
114 | uintptr(uint32(0)),
115 | )
116 |
117 | if ret != 0 {
118 | return nil, errors.Wrapf(err, "get extended tcp table failed code %x", ret)
119 | }
120 |
121 | return buffer, nil
122 | }
123 |
124 | return nil, errors.Wrapf(err, "get extended tcp table size failed code %x", ret)
125 | }
126 |
127 | // CreateJobObject uses the CreateJobObjectW Windows API Call to create and return a Handle to a new JobObject
128 | func CreateJobObject(attr *syscall.SecurityAttributes, name string) (syscall.Handle, error) {
129 | r1, _, err := procCreateJobObjectW.Call(
130 | uintptr(unsafe.Pointer(attr)),
131 | uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(name))),
132 | )
133 | if err != syscall.Errno(0) {
134 | return 0, err
135 | }
136 | return syscall.Handle(r1), nil
137 | }
138 |
139 | // StringToCharPtr converts a Go string into pointer to a null-terminated cstring.
140 | // This assumes the go string is already ANSI encoded.
141 | func StringToCharPtr(str string) *uint8 {
142 | chars := append([]byte(str), 0) // null terminated
143 | return &chars[0]
144 | }
145 |
146 | // StringToUTF16Ptr converts a Go string into a pointer to a null-terminated UTF-16 wide string.
147 | // This assumes str is of a UTF-8 compatible encoding so that it can be re-encoded as UTF-16.
148 | func StringToUTF16Ptr(str string) *uint16 {
149 | wchars := utf16.Encode([]rune(str + "\x00"))
150 | return &wchars[0]
151 | }
152 |
--------------------------------------------------------------------------------
/benchmark/README.md:
--------------------------------------------------------------------------------
1 | # [Go学习笔记(三) | 怎么写Go基准测试(性能测试)](https://razeen.me/post/go-how-to-write-benchmark.html)
2 |
3 |
4 |
5 | 或许你经常会思考这样的问题,我用不同的方法实现了同样的效果,哪个会更快?哪个内存消耗更小?这时候你一个简单的基准测试就能解决你的疑惑。
6 |
7 |
8 |
9 |
10 |
11 | Go向来是以工具丰富而著称的,在学习Go的过程中,你会发现无论是写一个单元测试,还是做一些竞争检测都能很快的上手,而且用的很痛快。当然,接下来要说的基准测试也一样。
12 |
13 | 基准测试工具就在Go的测试包中,下面就用[一个例子](https://github.com/razeencheng/demo-go/tree/master/benchmark)来介绍。
14 |
15 |
16 |
17 | ### 举个栗子
18 |
19 |
20 |
21 | 由于一些场景需要,我需要将`[]byte`输出16进制字符。
22 |
23 | 有时候我会这么写:
24 |
25 | ``` golang
26 | fmt.Sprintf("%x", b)
27 | ```
28 |
29 | 但有时候我会这么写:
30 |
31 | ```
32 | hex.EncodeToString(b)
33 | ```
34 |
35 | 但到底哪种写法更好呢?今天我就来比较一下。
36 |
37 | 直接写了个`main.go`
38 |
39 | ```Golang
40 | func EncodeA(b []byte) string {
41 | return fmt.Sprintf("%x", b)
42 | }
43 |
44 | func EncodeB(b []byte) string {
45 | return hex.EncodeToString(b)
46 | }
47 | ```
48 |
49 | 再写个测试`main_test.go`
50 |
51 | ```golang
52 | var buf = []byte("skdjadialsdgasadasdhsakdjsahlskdjagloqweiqwo")
53 |
54 | func BenchmarkEncodeA(b *testing.B) {
55 | for i := 0; i < b.N; i++ {
56 | EncodeA(buf)
57 | }
58 | }
59 |
60 | func BenchmarkEncodeB(b *testing.B) {
61 | for i := 0; i < b.N; i++ {
62 | EncodeB(buf)
63 | }
64 | }
65 | ```
66 |
67 |
68 |
69 | 就这么简单,我们的基本测试就写完了。从我的写法中你也许就知道:
70 |
71 | - 和单元测试一样,都写在`_test.go`文件中;
72 | - 需要以`Benchmark`为函数名开头;
73 | - 和单元测试类似,必须接受一个`*testing.B`参数;
74 | - 被测试代码放在一个循环中。
75 |
76 |
77 |
78 | 我们直接跑一下。当然我们也是用`go test`来执行测试,简单的测试只要带上`-bench=.`就可以了。
79 |
80 | ```Shell
81 | $ go test -bench=.
82 | goos: darwin
83 | goarch: amd64
84 | pkg: github.com/razeencheng/demo-go/benchmark
85 | BenchmarkEncodeA-8 5000000 265 ns/op
86 | BenchmarkEncodeB-8 10000000 161 ns/op
87 | PASS
88 | ok github.com/razeencheng/demo-go/benchmark 3.397s
89 | ```
90 |
91 | 前两行是平台信息,第三行包名。第四、五行就是测试的结果了。
92 |
93 | - `BenchmarkEncodeA-8 ` ,`BenchmarkEncodeB-8 ` 基准测试函数名-GOMAXPROCS
94 | - `5000000`,`10000000` 被测试的函数执行次数,也就是`EncodeA()`被执行了5000000次,`EncodeB()`被执行了10000000次,也就是`b.N`的值了。
95 | - `265 ns/op`,`161 ns/op`表示每次调用被测试函数花费的时间。
96 |
97 | 从花费的时间上来看,我们知道`EncodeB()`要快一点。
98 |
99 |
100 |
101 | ### 更多
102 |
103 | 你以为就这么简单的结束了么?NONONO。
104 |
105 | - `-bench` 可接收一个有效的正则表达式来执行符合条件的测试函数。当你的函数很多时,可以用它来过滤.
106 |
107 | ```Shell
108 | $ go test -bench=BenchmarkEncodeA
109 | goos: darwin
110 | goarch: amd64
111 | pkg: github.com/razeencheng/demo-go/benchmark
112 | BenchmarkEncodeA-8 5000000 256 ns/op
113 | PASS
114 | ok github.com/razeencheng/demo-go/benchmark 1.575s
115 | ```
116 |
117 | - ` -benchmem`可以查看内存分配
118 |
119 | ``` Shell
120 | $ go test -bench=. -benchmem
121 | goos: darwin
122 | goarch: amd64
123 | pkg: github.com/razeencheng/demo-go/benchmark
124 | BenchmarkEncodeA-8 5000000 261 ns/op 128 B/op 2 allocs/op
125 | BenchmarkEncodeB-8 10000000 162 ns/op 192 B/op 2 allocs/op
126 | PASS
127 | ok github.com/razeencheng/demo-go/benchmark 3.408s
128 | ```
129 |
130 | 其中`B/op` 表示每次执行会分配多少内存,`allocs/op`表示每次执行会发生多少次内存分配。
131 |
132 | - `-benchtime`指定每个测试执行的时间。默认`1s`,当你的函数比较耗时你可以设置更长一点。因为`b.N`是与这个时间有关的。
133 |
134 | 当你的运行时间没达到`-benchtime`制定的时间前,`b.N`将以1,2,5,10,20,50…增加,然后重新运行测试代码。
135 |
136 | ```Shell
137 | $ go test -bench=. -benchmem -benchtime=5s
138 | goos: darwin
139 | goarch: amd64
140 | pkg: github.com/razeencheng/demo-go/benchmark
141 | BenchmarkEncodeA-8 30000000 254 ns/op 128 B/op 2 allocs/op
142 | BenchmarkEncodeB-8 50000000 160 ns/op 192 B/op 2 allocs/op
143 | PASS
144 | ok github.com/razeencheng/demo-go/benchmark 16.113s
145 | ```
146 |
147 | - `-count`指定每个测试执行的次数。
148 |
149 | ```Shell
150 | $ go test -bench=. -benchmem -count=3
151 | goos: darwin
152 | goarch: amd64
153 | pkg: github.com/razeencheng/demo-go/benchmark
154 | BenchmarkEncodeA-8 5000000 256 ns/op 128 B/op 2 allocs/op
155 | BenchmarkEncodeA-8 5000000 255 ns/op 128 B/op 2 allocs/op
156 | BenchmarkEncodeA-8 5000000 253 ns/op 128 B/op 2 allocs/op
157 | BenchmarkEncodeB-8 10000000 163 ns/op 192 B/op 2 allocs/op
158 | BenchmarkEncodeB-8 10000000 160 ns/op 192 B/op 2 allocs/op
159 | BenchmarkEncodeB-8 10000000 160 ns/op 192 B/op 2 allocs/op
160 | PASS
161 | ok github.com/razeencheng/demo-go/benchmark 9.984s
162 | ```
163 |
164 | 我常用的也就这些了。
165 |
166 | 但对于`testing.B`来说,它拥有了`testing.T`的全部接口,所以`Fail,Skip,Error`这些都可以用,而且还增加了
167 |
168 | - `SetBytes( i uint64)` 统计内存消耗。
169 | - `SetParallelism(p int)` 制定并行数目。
170 | - `StartTimer / StopTimer / ResertTimer` 操作计时器。
171 |
172 | 你可以按需使用。
173 |
174 |
175 |
176 | ### 注意
177 |
178 | `b.N`为一个自增字段,谨慎用它做函数参数。
179 |
--------------------------------------------------------------------------------
/restful-api/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "net/http"
5 | "strconv"
6 | "time"
7 |
8 | "github.com/gin-gonic/contrib/sessions"
9 | "github.com/gin-gonic/gin"
10 | )
11 |
12 | func main() {
13 |
14 | r := gin.Default()
15 |
16 | store := sessions.NewCookieStore([]byte("dashdjkasdhaksda"))
17 | store.Options(sessions.Options{
18 | MaxAge: 7200,
19 | Path: "/",
20 | Secure: true,
21 | HttpOnly: true,
22 | })
23 | r.Use(sessions.Sessions("mydemo", store))
24 | r.NoRoute(func(c *gin.Context) { c.JSON(http.StatusNotFound, "Invaild api request") })
25 |
26 | v1 := r.Group("/api/v1")
27 | {
28 | v1.POST("/login", HandleLogin)
29 | v1.GET("/articles", HandleGetArticles)
30 |
31 | v1Auth := v1.Use(Auth)
32 | {
33 | v1Auth.POST("/logout", HandleLogout)
34 | v1Auth.POST("/articles", HandlePostArticles)
35 | v1Auth.PUT("/articles", HandleUpdateArticles)
36 | v1Auth.DELETE("/articles/:id", HandleDeleteArticles)
37 | v1.GET("/articles/:id/comments", HandleGetComments)
38 | v1Auth.POST("/articles/:id/comments", HandleAddComments)
39 | v1Auth.PUT("/articles/:id/comments/:id", HandleUpdateComments)
40 | v1Auth.DELETE("/articles/:id/comments/:id", HandleDeleteComments)
41 | }
42 | }
43 |
44 | r.Run(":8080")
45 | }
46 |
47 | const sessionsKey = "user"
48 |
49 | // Auth doc
50 | func Auth(c *gin.Context) {
51 | session := sessions.Default(c)
52 | u := session.Get(sessionsKey)
53 | if u == nil {
54 | c.JSON(http.StatusUnauthorized, &Resp{Error: "please login"})
55 | c.Abort()
56 | return
57 | }
58 | }
59 |
60 | // Resp doc
61 | type Resp struct {
62 | Data interface{} `json:"data"`
63 | Error string `json:"error"`
64 | }
65 |
66 | // LoginParams doc
67 | type LoginParams struct {
68 | UserID string `json:"user_id"`
69 | Password string `json:"password"`
70 | }
71 |
72 | // HandleLogin doc
73 | func HandleLogin(c *gin.Context) {
74 | param := &LoginParams{}
75 | if err := c.BindJSON(param); err != nil {
76 | c.JSON(http.StatusBadRequest, &Resp{Error: "parameters error"})
77 | return
78 | }
79 |
80 | // 做一些校验
81 | // ...
82 |
83 | session := sessions.Default(c)
84 | session.Set(sessionsKey, param.UserID)
85 | session.Save()
86 | c.JSON(http.StatusOK, &Resp{Data: "login succeed"})
87 | }
88 |
89 | // 模拟数据库
90 | var tempStorage = []*Article{}
91 |
92 | // HandleLogout doc
93 | func HandleLogout(c *gin.Context) {
94 | session := sessions.Default(c)
95 | session.Delete(sessionsKey)
96 | session.Save()
97 | c.JSON(http.StatusOK, &Resp{Data: "logout succeed"})
98 | }
99 |
100 | // HandleGetArticles doc
101 | func HandleGetArticles(c *gin.Context) {
102 |
103 | page := c.Query("page")
104 | pageSize := c.Query("page_size")
105 | orderby := c.Query("order")
106 | searchKey := c.Query("search")
107 |
108 | // 分页
109 | // 查询
110 | // 排序
111 | // ...
112 | _, _, _, _ = page, pageSize, orderby, searchKey
113 |
114 | c.JSON(http.StatusOK, &Resp{Data: map[string]interface{}{
115 | "result": tempStorage,
116 | "total": len(tempStorage),
117 | }})
118 | }
119 |
120 | // Article doc
121 | type Article struct {
122 | ID int `json:"id"`
123 | Titile string `json:"titile"`
124 | Tags []string `json:"tags"`
125 | Content string `json:"content"`
126 | UpdateAt time.Time `json:"update_at"`
127 | CreateAt time.Time `json:"create_at"`
128 | }
129 |
130 | // HandlePostArticles doc
131 | func HandlePostArticles(c *gin.Context) {
132 | param := &Article{}
133 | if err := c.BindJSON(param); err == nil {
134 | c.JSON(http.StatusBadRequest, &Resp{Error: "parameters error"})
135 | return
136 | }
137 |
138 | // 参数判断
139 | // 保存文章
140 | // ...
141 | param.ID = len(tempStorage)
142 | param.CreateAt = time.Now()
143 | tempStorage = append(tempStorage, param)
144 |
145 | c.JSON(http.StatusOK, &Resp{Data: param})
146 | }
147 |
148 | // HandleUpdateArticles doc
149 | func HandleUpdateArticles(c *gin.Context) {
150 | param := &Article{}
151 | if err := c.BindJSON(param); err == nil {
152 | c.JSON(http.StatusBadRequest, &Resp{Error: "parameters error"})
153 | return
154 | }
155 |
156 | // 参数判断
157 | // 保存文章
158 | // ...
159 | param.UpdateAt = time.Now()
160 | for i, v := range tempStorage {
161 | if v.ID == param.ID {
162 | param.CreateAt = v.CreateAt
163 | tempStorage[i] = param
164 | break
165 | }
166 | }
167 |
168 | c.JSON(http.StatusOK, &Resp{Data: param})
169 | }
170 |
171 | // HandleDeleteArticles doc
172 | func HandleDeleteArticles(c *gin.Context) {
173 | id, err := strconv.Atoi(c.Param("id"))
174 | if err != nil {
175 | c.JSON(http.StatusBadRequest, &Resp{Error: "parameters error"})
176 | return
177 | }
178 |
179 | // 删除
180 | // ...
181 | for i, v := range tempStorage {
182 | if v.ID == id {
183 | tempStorage = append(tempStorage[:i], tempStorage[i+1:]...)
184 | break
185 | }
186 | }
187 |
188 | c.JSON(http.StatusOK, &Resp{Data: "delete succeed"})
189 | }
190 |
191 | // HandleGetComments doc
192 | func HandleGetComments(c *gin.Context) {
193 |
194 | }
195 |
196 | // HandleAddComments doc
197 | func HandleAddComments(c *gin.Context) {
198 |
199 | }
200 |
201 | // HandleUpdateComments doc
202 | func HandleUpdateComments(c *gin.Context) {
203 |
204 | }
205 |
206 | // HandleDeleteComments doc
207 | func HandleDeleteComments(c *gin.Context) {
208 |
209 | }
210 |
--------------------------------------------------------------------------------
/swaggo-gin/docs/swagger.yaml:
--------------------------------------------------------------------------------
1 | basePath: /api/v1
2 | definitions:
3 | main.File:
4 | properties:
5 | id:
6 | type: integer
7 | len:
8 | type: integer
9 | name:
10 | type: string
11 | type: object
12 | main.Files:
13 | properties:
14 | files:
15 | items:
16 | $ref: '#/definitions/main.File'
17 | type: array
18 | len:
19 | type: integer
20 | type: object
21 | main.JSONParams:
22 | properties:
23 | array:
24 | description: 这是一个字符串数组
25 | items:
26 | type: string
27 | type: array
28 | int:
29 | description: 这是一个数字
30 | type: integer
31 | str:
32 | description: 这是一个字符串
33 | type: string
34 | struct:
35 | description: 这是一个结构
36 | properties:
37 | field:
38 | type: string
39 | type: object
40 | type: object
41 | host: 127.0.0.1:8080
42 | info:
43 | contact:
44 | email: me@razeen.me
45 | name: Razeen
46 | url: https://razeen.me
47 | description: |-
48 | # Test Example Makedown
49 |
50 | ### 关于使用说明
51 |
52 | 吧啦吧啦吧啦
53 |
54 | 
55 | license:
56 | name: Apache 2.0
57 | url: http://www.apache.org/licenses/LICENSE-2.0.html
58 | termsOfService: https://razeen.me
59 | title: Swagger Example API
60 | version: "1.0"
61 | paths:
62 | /file/{id}:
63 | get:
64 | consumes:
65 | - multipart/form-data
66 | description: 获取文件
67 | parameters:
68 | - description: 文件ID
69 | in: path
70 | name: id
71 | required: true
72 | type: integer
73 | produces:
74 | - application/octet-stream
75 | responses:
76 | "200":
77 | description: OK
78 | schema:
79 | type: string
80 | summary: 获取某个文件
81 | tags:
82 | - 文件处理
83 | /hello:
84 | get:
85 | consumes:
86 | - multipart/form-data
87 | description: 向你说Hello
88 | parameters:
89 | - description: 人名
90 | in: query
91 | name: who
92 | required: true
93 | type: string
94 | produces:
95 | - application/json
96 | responses:
97 | "200":
98 | description: '{"msg": "hello Razeen"}'
99 | schema:
100 | type: string
101 | "400":
102 | description: '{"msg": "who are you"}'
103 | schema:
104 | type: string
105 | summary: 测试SayHello
106 | tags:
107 | - 测试
108 | /json:
109 | post:
110 | consumes:
111 | - application/json
112 | description: 获取JSON的示例
113 | parameters:
114 | - description: 需要上传的JSON
115 | in: body
116 | name: param
117 | required: true
118 | schema:
119 | $ref: '#/definitions/main.JSONParams'
120 | produces:
121 | - application/json
122 | responses:
123 | "200":
124 | description: 返回
125 | schema:
126 | $ref: '#/definitions/main.JSONParams'
127 | summary: 获取JSON的示例
128 | tags:
129 | - JSON
130 | /list:
131 | get:
132 | consumes:
133 | - multipart/form-data
134 | description: 文件列表
135 | produces:
136 | - application/json
137 | responses:
138 | "200":
139 | description: OK
140 | schema:
141 | $ref: '#/definitions/main.Files'
142 | summary: 查看文件列表
143 | tags:
144 | - 文件处理
145 | /login:
146 | post:
147 | consumes:
148 | - multipart/form-data
149 | description: 登入
150 | parameters:
151 | - default: admin
152 | description: 用户名
153 | in: formData
154 | name: user
155 | required: true
156 | type: string
157 | - description: 密码
158 | in: formData
159 | name: password
160 | required: true
161 | type: string
162 | produces:
163 | - application/json
164 | responses:
165 | "200":
166 | description: '{"msg":"login success"}'
167 | schema:
168 | type: string
169 | "400":
170 | description: '{"msg": "user or password error"}'
171 | schema:
172 | type: string
173 | summary: 登陆
174 | tags:
175 | - 登陆
176 | /upload:
177 | post:
178 | consumes:
179 | - multipart/form-data
180 | description: 上传文件
181 | parameters:
182 | - description: 文件
183 | in: formData
184 | name: file
185 | required: true
186 | type: file
187 | produces:
188 | - application/json
189 | responses:
190 | "200":
191 | description: OK
192 | schema:
193 | $ref: '#/definitions/main.File'
194 | summary: 上传文件
195 | tags:
196 | - 文件处理
197 | schemes:
198 | - http
199 | - https
200 | swagger: "2.0"
201 | tags:
202 | - externalDocs:
203 | description: This is my blog site
204 | url: https://razeen.me
205 | name: TestTag1
206 | x-example-key:
207 | key: value
208 |
--------------------------------------------------------------------------------
/restful-api/README.md:
--------------------------------------------------------------------------------
1 | # [Golang中的RESTful API最佳实践](https://razeencheng.com/post/golang-and-restful-api.html)
2 |
3 |
4 | RESRful API已经流行很多年了,我也一直在使用它。最佳实践也看过不少,但当一个项目完成,再次回顾/梳理项目时,会发现很多API和规范还是多少有些出入。在这篇文章中,我们结合Go Web再次梳理一下RESTful API的相关最佳实践。
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | ### 关于RESTful API
13 |
14 | 关于什么是RESTful API,不再累述。推荐几个相关链接。
15 |
16 | - [理解RESTful架构](https://www.ruanyifeng.com/blog/2011/09/restful.html)
17 | - [REST API Tutorial](https://restfulapi.net/)
18 |
19 |
20 |
21 | ### 1.使用JSON
22 |
23 | 不管是接收还是返回数据都推荐使用JSON。
24 |
25 | 通常返回数据的格式有JSON和XML,但XML过于冗长,可读性差,而且各种语言的解析上也不如JSON,使用JSON的好处,显而易见。
26 |
27 | 而接收数据,我们这里也推荐使用JSON,对于后端开发而言,入参直接与模型绑定,省去冗长的参数解析能简化不少代码,而且JSON能更简单的传递一些更复杂的结构等。
28 |
29 | 正如示例代码中的这一段,我们以`gin`框架为例。
30 |
31 | ```go
32 | // HandleLogin doc
33 | func HandleLogin(c *gin.Context) {
34 | param := &LoginParams{}
35 | if err := c.BindJSON(param); err != nil {
36 | c.JSON(http.StatusBadRequest, &Resp{Error: "parameters error"})
37 | return
38 | }
39 |
40 | // 做一些校验
41 | // ...
42 |
43 | session := sessions.Default(c)
44 | session.Set(sessionsKey, param.UserID)
45 | session.Save()
46 | c.JSON(http.StatusOK, &Resp{Data: "login succeed"})
47 | }
48 | ```
49 |
50 | 通过`c.BindJSON`,轻松的将入参于模型`LoginParams`绑定;通过`c.JSON`轻松的将数据JSON序列化返回。
51 |
52 |
53 |
54 | 但所有接口都必须用JSON么?那也未必。比如文件上传,这时我们使用`FormData`比把文件base64之类的放到JSON里面更高效。
55 |
56 |
57 |
58 | ### 2.路径中不包含动词
59 |
60 | 我们的HTTP请求方法中已经有`GET`,`POST`等这些动作了,完全没有必要再路径中加上动词。
61 |
62 | 我们常用HTTP请求方法包括`GET`,`POST`,`PUT`和`DELETE`, 这也对应了我们经常需要做的数据库操作。`GET`查找/获取资源,`POST`新增资源,`PUT`修改资源,`DELETE`删除资源。
63 |
64 | 如下,这些路径中没有任何动词,简洁明了。
65 |
66 | ```go
67 | // 获取文章列表
68 | v1.GET("/articles", HandleGetArticles)
69 | // 发布文章
70 | v1.POST("/articles", HandlePostArticles)
71 | // 修改文章
72 | v1.PUT("/articles", HandleUpdateArticles)
73 | // 删除文章
74 | v1.DELETE("/articles/:id", HandleDeleteArticles)
75 | ```
76 |
77 |
78 |
79 | ### 3.路径中对应资源用复数
80 |
81 | 就像我们上面这段代码,`articles`对于的是我们的文章资源,背后就是一张数据库表`articles`, 所以操作这个资源的应该都用复数形式。
82 |
83 |
84 |
85 | ### 4.次要资源可分层展示
86 |
87 | 一个博客系统中,最主要的应该是文章了,而评论应该是其子资源,我们可以评论嵌套在它的父资源后面,如:
88 |
89 | ``` go
90 | // 获取评论列表
91 | v1.GET("/articles/:articles_id/comments", HandleGetComments)
92 | // 添加评论
93 | v1.POST("/articles/:articles_id/comments", HandleAddComments)
94 | // 修改评论
95 | v1.PUT("/articles/:articles_id/comments/:id", HandleUpdateComments)
96 | // 删除评论
97 | v1.DELETE("/articles/:articles_id/comments/:id", HandleDeleteComments)
98 | ```
99 |
100 | 那么,我们需要获取所有文章的评论怎么办?可以这么写:
101 |
102 | ``` go
103 | v1.GET("/articles/-/comments", HandleGetComments)
104 | ```
105 |
106 | 但这也不是决对的,资源虽然有层级关系,但这种层级关系不宜太深,个人感觉两层最多了,如果超过,可以直接拿出来放在一级。
107 |
108 |
109 |
110 | ### 5.分页、排序、过滤
111 |
112 | 获取列表时,会使用到分页、排序过滤。一般:
113 |
114 | ``` sh
115 | ?page=1&page_size=10 # 指定页面page与分页大小page_size
116 | ?sort=-create_at,+author # 按照创建时间create_at降序,作者author升序排序
117 | ?title=helloworld # 按字段title搜索
118 | ```
119 |
120 |
121 |
122 | ### 6.统一数据格式
123 |
124 | 不管是路径的格式,还是参数的格式,还是返回值的格式建议统一形式。
125 |
126 | 一般常用的格式有`蛇形`,`大驼峰`和`小驼峰`,个人比较喜欢`蛇形`。Anyway, 不管哪种,只要统一即可。
127 |
128 | 除了参数的命名统一外,返回的数据格式,最好统一,方便前端对接。
129 |
130 | 如下,我们定义`Resp`为通用返回数据结构,`Data`中存放反会的数据,如果出错,将错误信息放在`Error`中。
131 |
132 | ```go
133 | // Resp doc
134 | type Resp struct {
135 | Data interface{} `json:"data"`
136 | Error string `json:"error"`
137 | }
138 |
139 | // 登陆成功返回
140 | c.JSON(http.StatusOK, &Resp{Data: "login succeed"})
141 | // 查询列表
142 | c.JSON(http.StatusOK, &Resp{Data: map[string]interface{}{
143 | "result": tempStorage,
144 | "total": len(tempStorage),
145 | }})
146 | // 参数错误
147 | c.JSON(http.StatusBadRequest, &Resp{Error: "parameters error"})
148 | ```
149 |
150 |
151 |
152 | ### 7.善用HTTP状态码
153 |
154 | HTTP状态码有很多,我们没有必要也不可能全部用上,常用如下:
155 |
156 | - 200 StatusOK - 只有成功请求都返回200。
157 | - 400 StatusBadRequest - 当出现参数不对,用户参数校验不通过时,给出该状态,并返回Error
158 | - 401 StatusUnauthorized - 没有登陆/经过认证
159 | - 403 Forbidden - 服务端拒绝授权(如密码错误),不允许访问
160 | - 404 Not Found - 路径不存在
161 | - 500 Internal Server Error - 所请求的服务器遇到意外的情况并阻止其执行请求
162 | - 502 Bad Gateway - 网关或代理从上游接收到了无效的响应
163 | - 503 Service Unavailable - 服务器尚未处于可以接受请求的状态
164 |
165 | 其中`502`,`503`,我们写程序时并不会明确去抛出。所以我们平常用6个状态码已经能很好的展示服务端状态了。
166 |
167 | 同时,我们将状态与返回值对应起来,`200`状态下,返回`Data`数据;其他状态返回`Error`。
168 |
169 |
170 |
171 | ### 8.API版本化
172 |
173 | 正如Demo中所示,我们将路由分组到了`/api/v1`路径下面,版本化API。如果后续的服务端升级,但可能仍有很大部分客户端请求未升级,依然请求老版本的API,那么我们只需要增加`/api/v2`,然后在该路径下为已升级的客户端提供服务。这样,我们就做到了API的版本控制,可以平滑的从一个版本切换到另外一个版本。
174 |
175 | ```go
176 | v1 := r.Group("/api/v1")
177 | {
178 | v1.POST("/login", HandleLogin)
179 | v1.GET("/articles", HandleGetArticles)
180 | v1.GET("/articles/:id/comments", HandleGetComments)
181 | // ....
182 | ```
183 |
184 |
185 |
186 | ### 9. 统一 ‘/‘ 开头
187 |
188 | 所以路由中,路径都以’/‘开头,虽然框架会为我们做这件事,但还是建议统一加上。
189 |
190 |
191 |
192 | ### 10. 增加/更新操作 返回资源
193 |
194 | 对于`POST`,`PUT`操作,建议操作后,返回更新后的资源。
195 |
196 |
197 |
198 | ### 11. 使用HTTPS
199 |
200 | 对于暴露出去的接口/OpenAPI,一定使用HTTPS。一般时候,我们可以直接在服务前面架设一个WebServer,在WebServer内部署证书即可。当然,如果是直接由后端暴露出的接口,有必要直接在后端开启HTTPS!
201 |
202 |
203 |
204 | ### 12. 规范的API文档
205 |
206 | 对于我们这种前后端分离的架构,API文档是很重要。在Go中,我们很容易的能用swag结合代码注释自动生成API文档,在[ <使用swaggo自动生成Restful API文档>](https://razeencheng.com/post/go-swagger.html)中,我详细的介绍了怎么生成以及怎么写注释。
207 |
208 |
209 |
210 | ### 总结
211 |
212 | API写的好不好,重要的还是看是否遵循WEB标准和保持一致性,最终目的也是让这些API更清晰,易懂,安全,希望这些建议对你有所帮助。
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
--------------------------------------------------------------------------------
/swaggo-gin/handle.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "io"
6 | "net/http"
7 | "strconv"
8 |
9 | "github.com/gin-gonic/contrib/sessions"
10 | "github.com/gin-gonic/gin"
11 | )
12 |
13 | const isLoginKey = "is_login"
14 |
15 | // HandleAuth doc
16 | func HandleAuth(c *gin.Context) {
17 | session := sessions.Default(c)
18 | isLogin := session.Get(isLoginKey)
19 | if isLogin == nil || !isLogin.(bool) {
20 | c.JSON(http.StatusUnauthorized, gin.H{"msg": "please login"})
21 | c.Abort()
22 | return
23 | }
24 | }
25 |
26 | // HandleHello doc
27 | // @Summary 测试SayHello
28 | // @Description 向你说Hello
29 | // @Tags 测试
30 | // @Accept mpfd
31 | // @Produce json
32 | // @Param who query string true "人名"
33 | // @Success 200 {string} string "{"msg": "hello Razeen"}"
34 | // @Failure 400 {string} string "{"msg": "who are you"}"
35 | // @Router /hello [get]
36 | func HandleHello(c *gin.Context) {
37 | who := c.Query("who")
38 |
39 | if who == "" {
40 | c.JSON(http.StatusBadRequest, gin.H{"msg": "who are u?"})
41 | return
42 | }
43 |
44 | c.JSON(http.StatusOK, gin.H{"msg": "hello " + who})
45 | }
46 |
47 | // HandleLogin doc
48 | // @Summary 登陆
49 | // @Tags 登陆
50 | // @Description 登入
51 | // @Accept mpfd
52 | // @Produce json
53 | // @Param user formData string true "用户名" default(admin)
54 | // @Param password formData string true "密码"
55 | // @Success 200 {string} string "{"msg":"login success"}"
56 | // @Failure 400 {string} string "{"msg": "user or password error"}"
57 | // @Router /login [post]
58 | func HandleLogin(c *gin.Context) {
59 | user := c.PostForm("user")
60 | pwd := c.PostForm("password")
61 |
62 | if user == "admin" && pwd == "123456" {
63 | session := sessions.Default(c)
64 | session.Set(isLoginKey, true)
65 | session.Save()
66 | c.JSON(http.StatusOK, gin.H{"msg": "login success"})
67 | return
68 | }
69 |
70 | c.JSON(http.StatusUnauthorized, gin.H{"msg": "user or password error"})
71 | }
72 |
73 | // HandleUpload doc
74 | // @Summary 上传文件
75 | // @Tags 文件处理
76 | // @Description 上传文件
77 | // @Accept mpfd
78 | // @Produce json
79 | // @Param file formData file true "文件"
80 | // @Success 200 {object} main.File
81 | // @Router /upload [post]
82 | func HandleUpload(c *gin.Context) {
83 |
84 | fileHeader, err := c.FormFile("file")
85 | if err != nil {
86 | c.JSON(http.StatusBadRequest, gin.H{"msg": err})
87 | return
88 | }
89 |
90 | file, err := fileHeader.Open()
91 | if err != nil {
92 | c.JSON(http.StatusBadRequest, gin.H{"msg": err})
93 | return
94 | }
95 |
96 | fileCon := make([]byte, 1<<20)
97 | n, err := file.Read(fileCon)
98 | if err != nil {
99 | if err != io.EOF {
100 | c.JSON(http.StatusBadRequest, gin.H{"msg": err})
101 | return
102 | }
103 | }
104 |
105 | id++
106 | f := &File{ID: id, Name: fileHeader.Filename, Len: int(fileHeader.Size), Content: fileCon[:n]}
107 | files.Files = append(files.Files, f)
108 | files.Len++
109 | c.JSON(http.StatusOK, f)
110 | }
111 |
112 | var files = Files{Files: []*File{}}
113 | var id int
114 |
115 | // File doc
116 | type File struct {
117 | ID int `json:"id"`
118 | Name string `json:"name"`
119 | Len int `json:"len"`
120 | Content []byte `json:"-"`
121 | }
122 |
123 | // Files doc
124 | type Files struct {
125 | Files []*File `json:"files"`
126 | Len int `json:"len"`
127 | }
128 |
129 | // HandleList doc
130 | // @Summary 查看文件列表
131 | // @Tags 文件处理
132 | // @Description 文件列表
133 | // @Accept mpfd
134 | // @Produce json
135 | // @Success 200 {object} main.Files
136 | // @Router /list [get]
137 | func HandleList(c *gin.Context) {
138 | c.JSON(http.StatusOK, files)
139 | }
140 |
141 | // HandleGetFile doc
142 | // @Summary 获取某个文件
143 | // @Tags 文件处理
144 | // @Description 获取文件
145 | // @Accept mpfd
146 | // @Produce octet-stream
147 | // @Param id path integer true "文件ID"
148 | // @Success 200 {string} string ""
149 | // @Router /file/{id} [get]
150 | func HandleGetFile(c *gin.Context) {
151 | fid, err := strconv.Atoi(c.Param("id"))
152 | if err != nil {
153 | c.JSON(http.StatusBadRequest, gin.H{"msg": err})
154 | return
155 | }
156 |
157 | for _, f := range files.Files {
158 | if f.ID == fid {
159 | c.Writer.WriteHeader(http.StatusOK)
160 | c.Header("Access-Control-Expose-Headers", "Content-Disposition")
161 | c.Header("Content-Disposition", "attachment; "+f.Name)
162 | c.Header("Content-Type", "application/octet-stream")
163 | c.Header("Accept-Length", fmt.Sprintf("%d", len(f.Content)))
164 | c.Writer.Write(f.Content)
165 | return
166 | }
167 | }
168 |
169 | c.JSON(http.StatusBadRequest, gin.H{"msg": "no avail file"})
170 | }
171 |
172 | // JSONParams doc
173 | type JSONParams struct {
174 | // 这是一个字符串
175 | Str string `json:"str"`
176 | // 这是一个数字
177 | Int int `json:"int"`
178 | // 这是一个字符串数组
179 | Array []string `json:"array"`
180 | // 这是一个结构
181 | Struct struct {
182 | Field string `json:"field"`
183 | } `json:"struct"`
184 | }
185 |
186 | // HandleJSON doc
187 | // @Summary 获取JSON的示例
188 | // @Tags JSON
189 | // @Description 获取JSON的示例
190 | // @Accept json
191 | // @Produce json
192 | // @Param param body main.JSONParams true "需要上传的JSON"
193 | // @Success 200 {object} main.JSONParams "返回"
194 | // @Router /json [post]
195 | func HandleJSON(c *gin.Context) {
196 | param := JSONParams{}
197 | if err := c.BindJSON(¶m); err != nil {
198 | c.JSON(http.StatusBadRequest, gin.H{"msg": err.Error()})
199 | return
200 | }
201 |
202 | c.JSON(http.StatusOK, param)
203 | }
204 |
--------------------------------------------------------------------------------
/os-exec/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bufio"
5 | "context"
6 | "fmt"
7 | "log"
8 | "os/exec"
9 | "sync"
10 | "time"
11 | )
12 |
13 | func main() {
14 |
15 | _, err := exec.LookPath("./testcmd/testcmd")
16 | if err != nil {
17 | panic("可执行文件不可用 " + err.Error())
18 | }
19 |
20 | fmt.Println("\nRun Test 1")
21 | test1()
22 |
23 | fmt.Println("\nRun Test 2")
24 | test2()
25 |
26 | fmt.Println("\nRun Test 3")
27 | test3()
28 |
29 | fmt.Println("\nRun Test 4")
30 | test4()
31 |
32 | fmt.Println("\nRun Test 5")
33 | test5()
34 | }
35 |
36 | // 持续输入
37 | func test5() {
38 | cmd := exec.Command("openssl")
39 |
40 | stdout, _ := cmd.StdoutPipe()
41 | stderr, _ := cmd.StderrPipe()
42 |
43 | stdin, _ := cmd.StdinPipe()
44 |
45 | cmd.Start()
46 |
47 | // 读
48 | var wg sync.WaitGroup
49 | wg.Add(3)
50 | go func() {
51 | defer wg.Done()
52 | for {
53 | buf := make([]byte, 1024)
54 | n, err := stderr.Read(buf)
55 |
56 | if n > 0 {
57 | fmt.Println(string(buf[:n]))
58 | }
59 |
60 | if n == 0 {
61 | break
62 | }
63 |
64 | if err != nil {
65 | log.Printf("read err %v", err)
66 | return
67 | }
68 | }
69 | }()
70 |
71 | go func() {
72 | defer wg.Done()
73 | for {
74 | buf := make([]byte, 1024)
75 | n, err := stdout.Read(buf)
76 |
77 | if n == 0 {
78 | break
79 | }
80 |
81 | if n > 0 {
82 | fmt.Println(string(buf[:n]))
83 | }
84 |
85 | if n == 0 {
86 | break
87 | }
88 |
89 | if err != nil {
90 | log.Printf("read out %v", err)
91 | return
92 | }
93 |
94 | }
95 | }()
96 |
97 | // 写
98 | go func() {
99 | stdin.Write([]byte("version\n\n"))
100 | stdin.Write([]byte("ciphers -v\n\n"))
101 | stdin.Write([]byte("s_client -connect razeencheng.com:443"))
102 | stdin.Close()
103 | wg.Done()
104 | }()
105 |
106 | wg.Wait()
107 | err := cmd.Wait()
108 | if err != nil {
109 | log.Printf("cmd wait %v", err)
110 | return
111 | }
112 |
113 | }
114 |
115 | // 通过上下文控制超时
116 | func test4() {
117 |
118 | ctx, calcel := context.WithTimeout(context.Background(), 2*time.Second)
119 | defer calcel()
120 |
121 | cmd := exec.CommandContext(ctx, "./testcmd/testcmd", "-s", "-e")
122 |
123 | stdout, _ := cmd.StdoutPipe()
124 | stderr, _ := cmd.StderrPipe()
125 | oReader := bufio.NewReader(stdout)
126 | eReader := bufio.NewReader(stderr)
127 |
128 | cmd.Start()
129 |
130 | go func() {
131 | for {
132 | line, err := oReader.ReadString('\n')
133 |
134 | if line != "" {
135 | log.Printf("read line %s", line)
136 | }
137 |
138 | if err != nil || line == "" {
139 | log.Printf("read line err %v", err)
140 | return
141 | }
142 |
143 | }
144 | }()
145 |
146 | go func() {
147 | for {
148 | line, err := eReader.ReadString('\n')
149 |
150 | if line != "" {
151 | log.Printf("read err %s", line)
152 | }
153 |
154 | if err != nil || line == "" {
155 | log.Printf("read err %v", err)
156 | return
157 | }
158 |
159 | }
160 | }()
161 |
162 | err := cmd.Wait()
163 | if err != nil {
164 | log.Printf("cmd wait %v", err)
165 | return
166 | }
167 | }
168 |
169 | // 按行读输出的内容
170 | func test3() {
171 | cmd := exec.Command("./testcmd/testcmd", "-s", "-e")
172 | stdout, _ := cmd.StdoutPipe()
173 | stderr, _ := cmd.StderrPipe()
174 | oReader := bufio.NewReader(stdout)
175 | eReader := bufio.NewReader(stderr)
176 |
177 | cmd.Start()
178 |
179 | go func() {
180 | for {
181 | line, err := oReader.ReadString('\n')
182 |
183 | if line != "" {
184 | log.Printf("read line %s", line)
185 | }
186 |
187 | if err != nil || line == "" {
188 | log.Printf("read line err %v", err)
189 | return
190 | }
191 |
192 | }
193 | }()
194 |
195 | go func() {
196 | for {
197 | line, err := eReader.ReadString('\n')
198 |
199 | if line != "" {
200 | log.Printf("read err %s", line)
201 | }
202 |
203 | if err != nil || line == "" {
204 | log.Printf("read err %v", err)
205 | return
206 | }
207 |
208 | }
209 | }()
210 |
211 | err := cmd.Wait()
212 | if err != nil {
213 | log.Printf("cmd wait %v", err)
214 | return
215 | }
216 |
217 | }
218 |
219 | // stdout & stderr 分开输出
220 | func test2() {
221 | cmd := exec.Command("./testcmd/testcmd", "-s", "-e")
222 | stdout, _ := cmd.StdoutPipe()
223 | stderr, _ := cmd.StderrPipe()
224 | cmd.Start()
225 |
226 | go func() {
227 | for {
228 | buf := make([]byte, 1024)
229 | n, err := stderr.Read(buf)
230 |
231 | if n > 0 {
232 | log.Printf("read err %s", string(buf[:n]))
233 | }
234 |
235 | if n == 0 {
236 | break
237 | }
238 |
239 | if err != nil {
240 | log.Printf("read err %v", err)
241 | return
242 | }
243 | }
244 | }()
245 |
246 | go func() {
247 | for {
248 | buf := make([]byte, 1024)
249 | n, err := stdout.Read(buf)
250 |
251 | if n == 0 {
252 | break
253 | }
254 |
255 | if n > 0 {
256 | log.Printf("read out %s", string(buf[:n]))
257 |
258 | }
259 |
260 | if n == 0 {
261 | break
262 | }
263 |
264 | if err != nil {
265 | log.Printf("read out %v", err)
266 | return
267 | }
268 |
269 | }
270 | }()
271 |
272 | err := cmd.Wait()
273 | if err != nil {
274 | log.Printf("cmd wait %v", err)
275 | return
276 | }
277 |
278 | }
279 |
280 | // 简单执行
281 | func test1() {
282 | cmd := exec.Command("./testcmd/testcmd", "-s")
283 |
284 | // 使用CombinedOutput 将stdout stderr合并输出
285 | out, err := cmd.CombinedOutput()
286 | if err != nil {
287 | log.Printf("test1 failed %s\n", err)
288 | }
289 | log.Println("test1 output ", string(out))
290 | }
291 |
--------------------------------------------------------------------------------
/grpc/demo1/README.md:
--------------------------------------------------------------------------------
1 | # [gRPC在Go中的使用(一)Protocol Buffers语法与相关使用](https://razeen.me/post/how-to-use-grpc-in-golang-01.html)
2 |
3 | Desc:protobuf语法介绍,怎么写proto文件,grpc的使用入门
4 |
5 | 在gRPC官网用了一句话来介绍:“一个高性能、开源的通用RPC框架”,同时介绍了其四大特点:
6 |
7 | * 定义简单
8 | * 支持多种编程语言多种平台
9 | * 快速启动和缩放
10 | * 双向流媒体和集成身份验证
11 |
12 |
13 |
14 | 在`gRPC在go中使用`系列中,关于其简介与性能我就不多介绍,相信在社区也有很多关于这些的讨论。这里我主要从三个层次来总结我以往在Go中使用gRPC的一些经验,主要分为:
15 |
16 | 1. Protocol Buffers语法与相关使用
17 | 2. gRPC实现简单通讯
18 | 3. gRPC服务认证与双向流通讯
19 |
20 | **注:下面Protocol Buffers简写protobuf.*
21 |
22 |
23 | 这篇我们先介绍protobuf的相关语法、怎么书写`.proto`文件以及go代码生成。
24 |
25 | ### 简介
26 |
27 | 要熟练的使用GRPC,protobuf的熟练使用必不可少。
28 |
29 | gRPC使用[protobuf](https://github.com/google/protobuf)来定义服务。protobuf是由Google开发的一种数据序列化协议,可以把它想象成是XML或JSON格式,但更小,更快更简洁。而且一次定义,可生成多种语言的代码。
30 |
31 | ### 定义
32 |
33 | 首先我们需要编写一些`.proto`文件,定义我们在程序中需要处理的结构化数据。我们直接从一个实例开始讲起,下面是一个proto文件:
34 |
35 |
36 | ``` protobuf
37 | syntax = "proto3";
38 |
39 | option go_package = "github.com/razeencheng/demo-go/grpc/demo1/helloworld";
40 |
41 | package helloworld;
42 |
43 | import "github.com/golang/protobuf/ptypes/any/any.proto";
44 |
45 | message HelloWorldRequest {
46 | string greeting = 1;
47 | map infos = 2;
48 | }
49 |
50 | message HelloWorldResponse {
51 | string reply = 1;
52 | repeated google.protobuf.Any details = 2;
53 | }
54 |
55 | service HelloWorldService {
56 | rpc SayHelloWorld(HelloWorldRequest) returns (HelloWorldResponse){}
57 | }
58 | ```
59 |
60 |
61 | #### 版本
62 |
63 | 文件的开头`syntax="proto3"`也就指明版本,主要有`proto2`与`proto3`,他们在语法上有一定的差异,我这里主要使用的是后者。
64 |
65 | #### 包名
66 |
67 | 第二行,指定生成go文件的包名,可选项,默认使用第三行包名。
68 |
69 | 第三行,包名。
70 |
71 | #### 导包
72 |
73 | 第四行,类似你写go一样,protobuf也可以导入其他的包。
74 |
75 | #### 消息定义
76 |
77 | 后面message开头的两个结构就是我们需要传递的消息类型。所有的消息类型都是以`message`开始,然后定义类型名称。结构内字段的定义为`字段规则 字段类型 字段名=字段编号`
78 |
79 | - 字段规则主要有 `singular`和`repeated`。如其中`greeting`和`reply`的字段规则为`singular`,允许该消息中出现0个或1个该字段(但不能超过一个),而像`details`字段允许重复任意次数。其实对应到go里面也就是基本类型和切片类型。
80 | - 字段类型,下表是proto内类型与go类型的对应表。
81 |
82 | | .proto Type | Notes | Go Type |
83 | | ----------- | ------------------------------------------------------------ | ------- |
84 | | double | | float64 |
85 | | float | | float32 |
86 | | int32 | 使用可变长度编码。 无效编码负数 - 如果您的字段可能具有负值, 请改用sint32。 | int32 |
87 | | int64 | 使用可变长度编码。 无效编码负数 - 如果您的字段可能具有负值,请改用sint64。 | int64 |
88 | | uint32 | 使用可变长度编码。 | uint32 |
89 | | uint64 | 使用可变长度编码。 | uint64 |
90 | | sint32 | 使用可变长度编码。 带符号的int值。 这些比常规的int32更有效地编码负数。 | int32 |
91 | | sint64 | 使用可变长度编码。 带符号的int值。 这些比常规的int64更有效地编码负数。 | int64 |
92 | | fixed32 | 总是四个字节。 如果值通常大于228,则比uint32效率更高。 | uint32 |
93 | | fixed64 | 总是八个字节。 如果值通常大于256,则会比uint64更高效。 | uint64 |
94 | | sfixed32 | 总是四个字节。 | int32 |
95 | | sfixed64 | 总是八个字节。 | int64 |
96 | | bool | | bool |
97 | | string | 字符串必须始终包含UTF-8编码或7位ASCII文本。 | string |
98 | | bytes | 可能包含任何字节序列。 | []byte |
99 |
100 | 看到这里你也许会疑惑,go里面的切片,map,接口等类型我怎么定义呢?别急,下面一一替你解答。
101 |
102 | 1.map类型,`HelloWorldRequest`的`infos`就是一个map类型,它的结构为`map map_field = N` 但是在使用的时候你需要注意map类型不能`repetead`。
103 |
104 | 2.切片类型,我们直接定义其规则为`repeated`就可以了。就像`HelloWorldResponse`中的`details`字段一样,它就是一个切片类型。那么你会问了它是什么类型的切片?这就看下面了~
105 |
106 | 3.接口类型在proto中没有直接实现,但在[google/protobuf/any.proto](https://github.com/golang/protobuf/blob/master/ptypes/any/any.proto)中定义了一个`google.protobuf.Any`类型,然后结合[protobuf/go](https://github.com/golang/protobuf/blob/master/ptypes/any.go)也算是曲线救国了~
107 |
108 | - 字段编号
109 |
110 | 最后的1,2代表的是每个字段在该消息中的唯一标签,在与消息二进制格式中标识这些字段,而且当你的消息在使用的时候该值不能改变。1到15都是用一个字节编码的,通常用于标签那些频繁发生修改的字段。16到2047用两个字节编码,最大的是2^29-1(536,870,911),其中19000-19999为预留的,你也不可使用。
111 |
112 |
113 |
114 | #### 服务定义
115 |
116 | 如果你要使用RPC(远程过程调用)系统的消息类型,那就需要定义RPC服务接口,protobuf编译器将会根据所选择的不同语言生成服务接口代码及存根。就如:
117 |
118 | ```
119 | service HelloWorldService {
120 | rpc SayHelloWorld(HelloWorldRequest) returns (HelloWorldResponse){}
121 | }
122 | ```
123 |
124 | protobuf编译器将产生一个抽象接口`HelloWorldService`以及一个相应的存根实现。存根将所有的调用指向RpcChannel(SayHelloWorld),它是一个抽象接口,必须在RPC系统中对该接口进行实现。具体如何使用,将在下一篇博客中详细介绍。
125 |
126 |
127 | ### 生成Go代码
128 |
129 | #### 安装protoc
130 |
131 | 首先要安装`protoc`,可直接到[这里](https://github.com/google/protobuf/releases/tag/v3.0.0)下载二进制安装到 `$PATH`里面,也可以直接下载源码编译。除此之外,你还需要安装go的proto插件`protoc-gen-go`。
132 |
133 | ```Go
134 | // mac terminal
135 | go get -u github.com/golang/protobuf/{proto,protoc-gen-go}
136 | // win powershell
137 | go get -u github.com/golang/protobuf/proto
138 | go get -u github.com/golang/protobuf/protoc-gen-go
139 | ```
140 |
141 | #### 生成go代码
142 |
143 | 接下来,使用`protoc`命令即可生成。
144 |
145 | ```Bash
146 | ### mac terminal
147 | protoc -I ${GOPATH}/src --go_out=plugins=grpc:${GOPATH}/src ${GOPATH}/src/github.com/razeencheng/demo-go/grpc/demo1/helloworld/hello_world.proto
148 | ### win powershell
149 | protoc -I $env:GOPATH\src --go_out=plugins=grpc:$env:GOPATH\src $env:GOPATH\src\github.com\razeencheng\demo-go\grpc\demo1\helloworld\hello_world.proto
150 | ```
151 |
152 | 如上所示 `-I`指定搜索proto文件的目录,`--go_out=plugins=grpc:`指定生成go代码的文件夹,后面就是需要生成的proto文件路径。
153 |
154 | > *注意:* 如果你使用到了其他包的结构,`-I`需要将该资源包括在内。
155 | >
156 | > 例如我导入了`github.com/golang/protobuf/ptypes/any/any.proto` 我首先需要
157 | >
158 | > `go get -u github.com/golang/protobuf`获取该包,然后在使用时资源路径(`-I`)直接为`GOPATH\src`。
159 |
160 | 最后生成的`hello-world.pb.go`文件。内容大概如下图所示,点[这里](https://github.com/razeencheng/demo-go/blob/master/grpc/demo1/helloworld/hello_world.pb.go)可查看全部。
161 |
162 |
163 |
164 |
165 |
166 | 图中我们可以看到两个`message`对应生成了两个结构体,同时会生成一些序列化的方法等。
167 |
168 |
169 |
170 |
171 |
172 | 而定义的`service`则是生成了对应的`client`与`server`接口,那么这到底有什么用?怎么去用呢?[下一篇博客](https://razeen.me/post/how-to-use-grpc-in-golang-02.html)将为你详细讲解~
173 |
174 |
175 | 看到这,我们简单的了解一下protobuf语法,如果你想了解更多,点[这里](https://developers.google.com/protocol-buffers/docs/proto3)看官方文档。
176 |
--------------------------------------------------------------------------------
/restful-api/go.sum:
--------------------------------------------------------------------------------
1 | github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff h1:RmdPFa+slIr4SCBg4st/l/vZWVe9QJKMXGO60Bxbe04=
2 | github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw=
3 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
4 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
6 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
7 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
8 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
9 | github.com/gin-gonic/contrib v0.0.0-20201101042839-6a891bf89f19 h1:J2LPEOcQmWaooBnBtUDV9KHFEnP5LYTZY03GiQ0oQBw=
10 | github.com/gin-gonic/contrib v0.0.0-20201101042839-6a891bf89f19/go.mod h1:iqneQ2Df3omzIVTkIfn7c1acsVnMGiSLn4XF5Blh3Yg=
11 | github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
12 | github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
13 | github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
14 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
15 | github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
16 | github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
17 | github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
18 | github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
19 | github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw=
20 | github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
21 | github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
22 | github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
23 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
24 | github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
25 | github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
26 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
27 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
28 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
29 | github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
30 | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
31 | github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
32 | github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
33 | github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
34 | github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
35 | github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
36 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
37 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
38 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
39 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
40 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
41 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
42 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
43 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
44 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
45 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
46 | github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
47 | github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
48 | github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
49 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
50 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
51 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
52 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
53 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
54 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
55 | github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw=
56 | github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI=
57 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
58 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
59 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
60 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
61 | github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
62 | github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
63 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
64 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
65 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
66 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
67 | github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
68 | github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
69 | github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
70 | github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
71 | github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
72 | golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
73 | golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
74 | golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
75 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
76 | golang.org/x/net v0.0.0-20220621193019-9d032be2e588 h1:9ubFuySsnAJYGyJrZ3koiEv8FyqofCBdz3G9Mbf2YFc=
77 | golang.org/x/net v0.0.0-20220621193019-9d032be2e588/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
78 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
79 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
80 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
81 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
82 | golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
83 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
84 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
85 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
86 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
87 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
88 | golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
89 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
90 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
91 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
92 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
93 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
94 | google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
95 | google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
96 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
97 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
98 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
99 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
100 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
101 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
102 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
103 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
104 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
105 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
106 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
107 |
--------------------------------------------------------------------------------
/grpc/demo1/helloworld/hello_world.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go. DO NOT EDIT.
2 | // source: github.com/razeencheng/demo-go/grpc/demo1/helloworld/hello_world.proto
3 |
4 | package helloworld // import "github.com/razeencheng/demo-go/grpc/demo1/helloworld"
5 |
6 | import proto "github.com/golang/protobuf/proto"
7 | import fmt "fmt"
8 | import math "math"
9 | import any "github.com/golang/protobuf/ptypes/any"
10 |
11 | import (
12 | context "golang.org/x/net/context"
13 | grpc "google.golang.org/grpc"
14 | )
15 |
16 | // Reference imports to suppress errors if they are not otherwise used.
17 | var _ = proto.Marshal
18 | var _ = fmt.Errorf
19 | var _ = math.Inf
20 |
21 | // This is a compile-time assertion to ensure that this generated file
22 | // is compatible with the proto package it is being compiled against.
23 | // A compilation error at this line likely means your copy of the
24 | // proto package needs to be updated.
25 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
26 |
27 | type HelloWorldRequest struct {
28 | Greeting string `protobuf:"bytes,1,opt,name=greeting,proto3" json:"greeting,omitempty"`
29 | Infos map[string]string `protobuf:"bytes,2,rep,name=infos,proto3" json:"infos,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
30 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
31 | XXX_unrecognized []byte `json:"-"`
32 | XXX_sizecache int32 `json:"-"`
33 | }
34 |
35 | func (m *HelloWorldRequest) Reset() { *m = HelloWorldRequest{} }
36 | func (m *HelloWorldRequest) String() string { return proto.CompactTextString(m) }
37 | func (*HelloWorldRequest) ProtoMessage() {}
38 | func (*HelloWorldRequest) Descriptor() ([]byte, []int) {
39 | return fileDescriptor_hello_world_5bbefe74e8f48f56, []int{0}
40 | }
41 | func (m *HelloWorldRequest) XXX_Unmarshal(b []byte) error {
42 | return xxx_messageInfo_HelloWorldRequest.Unmarshal(m, b)
43 | }
44 | func (m *HelloWorldRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
45 | return xxx_messageInfo_HelloWorldRequest.Marshal(b, m, deterministic)
46 | }
47 | func (dst *HelloWorldRequest) XXX_Merge(src proto.Message) {
48 | xxx_messageInfo_HelloWorldRequest.Merge(dst, src)
49 | }
50 | func (m *HelloWorldRequest) XXX_Size() int {
51 | return xxx_messageInfo_HelloWorldRequest.Size(m)
52 | }
53 | func (m *HelloWorldRequest) XXX_DiscardUnknown() {
54 | xxx_messageInfo_HelloWorldRequest.DiscardUnknown(m)
55 | }
56 |
57 | var xxx_messageInfo_HelloWorldRequest proto.InternalMessageInfo
58 |
59 | func (m *HelloWorldRequest) GetGreeting() string {
60 | if m != nil {
61 | return m.Greeting
62 | }
63 | return ""
64 | }
65 |
66 | func (m *HelloWorldRequest) GetInfos() map[string]string {
67 | if m != nil {
68 | return m.Infos
69 | }
70 | return nil
71 | }
72 |
73 | type HelloWorldResponse struct {
74 | Reply string `protobuf:"bytes,1,opt,name=reply,proto3" json:"reply,omitempty"`
75 | Details []*any.Any `protobuf:"bytes,2,rep,name=details,proto3" json:"details,omitempty"`
76 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
77 | XXX_unrecognized []byte `json:"-"`
78 | XXX_sizecache int32 `json:"-"`
79 | }
80 |
81 | func (m *HelloWorldResponse) Reset() { *m = HelloWorldResponse{} }
82 | func (m *HelloWorldResponse) String() string { return proto.CompactTextString(m) }
83 | func (*HelloWorldResponse) ProtoMessage() {}
84 | func (*HelloWorldResponse) Descriptor() ([]byte, []int) {
85 | return fileDescriptor_hello_world_5bbefe74e8f48f56, []int{1}
86 | }
87 | func (m *HelloWorldResponse) XXX_Unmarshal(b []byte) error {
88 | return xxx_messageInfo_HelloWorldResponse.Unmarshal(m, b)
89 | }
90 | func (m *HelloWorldResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
91 | return xxx_messageInfo_HelloWorldResponse.Marshal(b, m, deterministic)
92 | }
93 | func (dst *HelloWorldResponse) XXX_Merge(src proto.Message) {
94 | xxx_messageInfo_HelloWorldResponse.Merge(dst, src)
95 | }
96 | func (m *HelloWorldResponse) XXX_Size() int {
97 | return xxx_messageInfo_HelloWorldResponse.Size(m)
98 | }
99 | func (m *HelloWorldResponse) XXX_DiscardUnknown() {
100 | xxx_messageInfo_HelloWorldResponse.DiscardUnknown(m)
101 | }
102 |
103 | var xxx_messageInfo_HelloWorldResponse proto.InternalMessageInfo
104 |
105 | func (m *HelloWorldResponse) GetReply() string {
106 | if m != nil {
107 | return m.Reply
108 | }
109 | return ""
110 | }
111 |
112 | func (m *HelloWorldResponse) GetDetails() []*any.Any {
113 | if m != nil {
114 | return m.Details
115 | }
116 | return nil
117 | }
118 |
119 | func init() {
120 | proto.RegisterType((*HelloWorldRequest)(nil), "helloworld.HelloWorldRequest")
121 | proto.RegisterMapType((map[string]string)(nil), "helloworld.HelloWorldRequest.InfosEntry")
122 | proto.RegisterType((*HelloWorldResponse)(nil), "helloworld.HelloWorldResponse")
123 | }
124 |
125 | // Reference imports to suppress errors if they are not otherwise used.
126 | var _ context.Context
127 | var _ grpc.ClientConn
128 |
129 | // This is a compile-time assertion to ensure that this generated file
130 | // is compatible with the grpc package it is being compiled against.
131 | const _ = grpc.SupportPackageIsVersion4
132 |
133 | // HelloWorldServiceClient is the client API for HelloWorldService service.
134 | //
135 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
136 | type HelloWorldServiceClient interface {
137 | SayHelloWorld(ctx context.Context, in *HelloWorldRequest, opts ...grpc.CallOption) (*HelloWorldResponse, error)
138 | }
139 |
140 | type helloWorldServiceClient struct {
141 | cc *grpc.ClientConn
142 | }
143 |
144 | func NewHelloWorldServiceClient(cc *grpc.ClientConn) HelloWorldServiceClient {
145 | return &helloWorldServiceClient{cc}
146 | }
147 |
148 | func (c *helloWorldServiceClient) SayHelloWorld(ctx context.Context, in *HelloWorldRequest, opts ...grpc.CallOption) (*HelloWorldResponse, error) {
149 | out := new(HelloWorldResponse)
150 | err := c.cc.Invoke(ctx, "/helloworld.HelloWorldService/SayHelloWorld", in, out, opts...)
151 | if err != nil {
152 | return nil, err
153 | }
154 | return out, nil
155 | }
156 |
157 | // HelloWorldServiceServer is the server API for HelloWorldService service.
158 | type HelloWorldServiceServer interface {
159 | SayHelloWorld(context.Context, *HelloWorldRequest) (*HelloWorldResponse, error)
160 | }
161 |
162 | func RegisterHelloWorldServiceServer(s *grpc.Server, srv HelloWorldServiceServer) {
163 | s.RegisterService(&_HelloWorldService_serviceDesc, srv)
164 | }
165 |
166 | func _HelloWorldService_SayHelloWorld_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
167 | in := new(HelloWorldRequest)
168 | if err := dec(in); err != nil {
169 | return nil, err
170 | }
171 | if interceptor == nil {
172 | return srv.(HelloWorldServiceServer).SayHelloWorld(ctx, in)
173 | }
174 | info := &grpc.UnaryServerInfo{
175 | Server: srv,
176 | FullMethod: "/helloworld.HelloWorldService/SayHelloWorld",
177 | }
178 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
179 | return srv.(HelloWorldServiceServer).SayHelloWorld(ctx, req.(*HelloWorldRequest))
180 | }
181 | return interceptor(ctx, in, info, handler)
182 | }
183 |
184 | var _HelloWorldService_serviceDesc = grpc.ServiceDesc{
185 | ServiceName: "helloworld.HelloWorldService",
186 | HandlerType: (*HelloWorldServiceServer)(nil),
187 | Methods: []grpc.MethodDesc{
188 | {
189 | MethodName: "SayHelloWorld",
190 | Handler: _HelloWorldService_SayHelloWorld_Handler,
191 | },
192 | },
193 | Streams: []grpc.StreamDesc{},
194 | Metadata: "github.com/razeencheng/demo-go/grpc/demo1/helloworld/hello_world.proto",
195 | }
196 |
197 | func init() {
198 | proto.RegisterFile("github.com/razeencheng/demo-go/grpc/demo1/helloworld/hello_world.proto", fileDescriptor_hello_world_5bbefe74e8f48f56)
199 | }
200 |
201 | var fileDescriptor_hello_world_5bbefe74e8f48f56 = []byte{
202 | // 316 bytes of a gzipped FileDescriptorProto
203 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x51, 0x41, 0x4b, 0xf3, 0x40,
204 | 0x10, 0xfd, 0xd2, 0xd2, 0x4f, 0x1d, 0x11, 0x74, 0xe9, 0xa1, 0x04, 0x94, 0xd2, 0x53, 0x2f, 0xee,
205 | 0x62, 0x15, 0x29, 0x1e, 0x04, 0x05, 0x45, 0x6f, 0x92, 0x1e, 0x84, 0x5e, 0x24, 0x4d, 0xa7, 0xdb,
206 | 0xe0, 0x76, 0x27, 0x6e, 0x36, 0x95, 0xf8, 0x8f, 0xfc, 0x97, 0xb2, 0x59, 0x63, 0x0a, 0xa2, 0x07,
207 | 0x0f, 0x81, 0x79, 0x33, 0xef, 0xcd, 0x7b, 0x99, 0x85, 0x5b, 0x99, 0xda, 0x65, 0x31, 0xe3, 0x09,
208 | 0xad, 0x84, 0x89, 0xdf, 0x10, 0x75, 0xb2, 0x44, 0x2d, 0xc5, 0x1c, 0x57, 0x74, 0x2c, 0x49, 0x48,
209 | 0x93, 0x25, 0x15, 0x38, 0x11, 0x4b, 0x54, 0x8a, 0x5e, 0xc9, 0xa8, 0xb9, 0x2f, 0x9f, 0xaa, 0x9a,
210 | 0x67, 0x86, 0x2c, 0x31, 0x68, 0xa6, 0xa1, 0xd8, 0xd8, 0x29, 0x49, 0xc5, 0x5a, 0x8a, 0x8a, 0x34,
211 | 0x2b, 0x16, 0x22, 0xb3, 0x65, 0x86, 0xb9, 0x88, 0x75, 0xe9, 0x3e, 0x2f, 0x1e, 0xbc, 0x07, 0x70,
212 | 0x70, 0xe7, 0xf4, 0x8f, 0x4e, 0x1f, 0xe1, 0x4b, 0x81, 0xb9, 0x65, 0x21, 0x6c, 0x4b, 0x83, 0x68,
213 | 0x53, 0x2d, 0x7b, 0x41, 0x3f, 0x18, 0xee, 0x44, 0x5f, 0x98, 0x5d, 0x42, 0x27, 0xd5, 0x0b, 0xca,
214 | 0x7b, 0xad, 0x7e, 0x7b, 0xb8, 0x3b, 0x1a, 0xf2, 0xc6, 0x9e, 0x7f, 0xdb, 0xc4, 0xef, 0x1d, 0xf5,
215 | 0x46, 0x5b, 0x53, 0x46, 0x5e, 0x16, 0x8e, 0x01, 0x9a, 0x26, 0xdb, 0x87, 0xf6, 0x33, 0x96, 0x9f,
216 | 0x26, 0xae, 0x64, 0x5d, 0xe8, 0xac, 0x63, 0x55, 0x60, 0xaf, 0x55, 0xf5, 0x3c, 0xb8, 0x68, 0x8d,
217 | 0x83, 0xc1, 0x14, 0xd8, 0xa6, 0x41, 0x9e, 0x91, 0xce, 0xd1, 0xf1, 0x0d, 0x66, 0xaa, 0xde, 0xe1,
218 | 0x01, 0xe3, 0xb0, 0x35, 0x47, 0x1b, 0xa7, 0xaa, 0xce, 0xd9, 0xe5, 0x92, 0x48, 0x2a, 0xe4, 0xf5,
219 | 0x3d, 0xf8, 0x95, 0x2e, 0xa3, 0x9a, 0x34, 0xc2, 0xcd, 0x33, 0x4c, 0xd0, 0xac, 0xd3, 0x04, 0xd9,
220 | 0x03, 0xec, 0x4d, 0xe2, 0xb2, 0xe9, 0xb3, 0xc3, 0x5f, 0x7f, 0x36, 0x3c, 0xfa, 0x69, 0xec, 0xa3,
221 | 0x0e, 0xfe, 0x5d, 0x9f, 0x4f, 0xcf, 0xfe, 0xf2, 0xea, 0xb3, 0xff, 0x55, 0xea, 0xd3, 0x8f, 0x00,
222 | 0x00, 0x00, 0xff, 0xff, 0x38, 0x6b, 0xfd, 0x41, 0x34, 0x02, 0x00, 0x00,
223 | }
224 |
--------------------------------------------------------------------------------
/swaggo-gin/docs/swagger.json:
--------------------------------------------------------------------------------
1 | {
2 | "schemes": [
3 | "http",
4 | "https"
5 | ],
6 | "swagger": "2.0",
7 | "info": {
8 | "description": "# Test Example Makedown\n\n### 关于使用说明\n\n吧啦吧啦吧啦\n\n",
9 | "title": "Swagger Example API",
10 | "termsOfService": "https://razeen.me",
11 | "contact": {
12 | "name": "Razeen",
13 | "url": "https://razeen.me",
14 | "email": "me@razeen.me"
15 | },
16 | "license": {
17 | "name": "Apache 2.0",
18 | "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
19 | },
20 | "version": "1.0"
21 | },
22 | "host": "127.0.0.1:8080",
23 | "basePath": "/api/v1",
24 | "paths": {
25 | "/file/{id}": {
26 | "get": {
27 | "description": "获取文件",
28 | "consumes": [
29 | "multipart/form-data"
30 | ],
31 | "produces": [
32 | "application/octet-stream"
33 | ],
34 | "tags": [
35 | "文件处理"
36 | ],
37 | "summary": "获取某个文件",
38 | "parameters": [
39 | {
40 | "type": "integer",
41 | "description": "文件ID",
42 | "name": "id",
43 | "in": "path",
44 | "required": true
45 | }
46 | ],
47 | "responses": {
48 | "200": {
49 | "description": "OK",
50 | "schema": {
51 | "type": "string"
52 | }
53 | }
54 | }
55 | }
56 | },
57 | "/hello": {
58 | "get": {
59 | "description": "向你说Hello",
60 | "consumes": [
61 | "multipart/form-data"
62 | ],
63 | "produces": [
64 | "application/json"
65 | ],
66 | "tags": [
67 | "测试"
68 | ],
69 | "summary": "测试SayHello",
70 | "parameters": [
71 | {
72 | "type": "string",
73 | "description": "人名",
74 | "name": "who",
75 | "in": "query",
76 | "required": true
77 | }
78 | ],
79 | "responses": {
80 | "200": {
81 | "description": "{\"msg\": \"hello Razeen\"}",
82 | "schema": {
83 | "type": "string"
84 | }
85 | },
86 | "400": {
87 | "description": "{\"msg\": \"who are you\"}",
88 | "schema": {
89 | "type": "string"
90 | }
91 | }
92 | }
93 | }
94 | },
95 | "/json": {
96 | "post": {
97 | "description": "获取JSON的示例",
98 | "consumes": [
99 | "application/json"
100 | ],
101 | "produces": [
102 | "application/json"
103 | ],
104 | "tags": [
105 | "JSON"
106 | ],
107 | "summary": "获取JSON的示例",
108 | "parameters": [
109 | {
110 | "description": "需要上传的JSON",
111 | "name": "param",
112 | "in": "body",
113 | "required": true,
114 | "schema": {
115 | "$ref": "#/definitions/main.JSONParams"
116 | }
117 | }
118 | ],
119 | "responses": {
120 | "200": {
121 | "description": "返回",
122 | "schema": {
123 | "$ref": "#/definitions/main.JSONParams"
124 | }
125 | }
126 | }
127 | }
128 | },
129 | "/list": {
130 | "get": {
131 | "description": "文件列表",
132 | "consumes": [
133 | "multipart/form-data"
134 | ],
135 | "produces": [
136 | "application/json"
137 | ],
138 | "tags": [
139 | "文件处理"
140 | ],
141 | "summary": "查看文件列表",
142 | "responses": {
143 | "200": {
144 | "description": "OK",
145 | "schema": {
146 | "$ref": "#/definitions/main.Files"
147 | }
148 | }
149 | }
150 | }
151 | },
152 | "/login": {
153 | "post": {
154 | "description": "登入",
155 | "consumes": [
156 | "multipart/form-data"
157 | ],
158 | "produces": [
159 | "application/json"
160 | ],
161 | "tags": [
162 | "登陆"
163 | ],
164 | "summary": "登陆",
165 | "parameters": [
166 | {
167 | "type": "string",
168 | "default": "admin",
169 | "description": "用户名",
170 | "name": "user",
171 | "in": "formData",
172 | "required": true
173 | },
174 | {
175 | "type": "string",
176 | "description": "密码",
177 | "name": "password",
178 | "in": "formData",
179 | "required": true
180 | }
181 | ],
182 | "responses": {
183 | "200": {
184 | "description": "{\"msg\":\"login success\"}",
185 | "schema": {
186 | "type": "string"
187 | }
188 | },
189 | "400": {
190 | "description": "{\"msg\": \"user or password error\"}",
191 | "schema": {
192 | "type": "string"
193 | }
194 | }
195 | }
196 | }
197 | },
198 | "/upload": {
199 | "post": {
200 | "description": "上传文件",
201 | "consumes": [
202 | "multipart/form-data"
203 | ],
204 | "produces": [
205 | "application/json"
206 | ],
207 | "tags": [
208 | "文件处理"
209 | ],
210 | "summary": "上传文件",
211 | "parameters": [
212 | {
213 | "type": "file",
214 | "description": "文件",
215 | "name": "file",
216 | "in": "formData",
217 | "required": true
218 | }
219 | ],
220 | "responses": {
221 | "200": {
222 | "description": "OK",
223 | "schema": {
224 | "$ref": "#/definitions/main.File"
225 | }
226 | }
227 | }
228 | }
229 | }
230 | },
231 | "definitions": {
232 | "main.File": {
233 | "type": "object",
234 | "properties": {
235 | "id": {
236 | "type": "integer"
237 | },
238 | "len": {
239 | "type": "integer"
240 | },
241 | "name": {
242 | "type": "string"
243 | }
244 | }
245 | },
246 | "main.Files": {
247 | "type": "object",
248 | "properties": {
249 | "files": {
250 | "type": "array",
251 | "items": {
252 | "$ref": "#/definitions/main.File"
253 | }
254 | },
255 | "len": {
256 | "type": "integer"
257 | }
258 | }
259 | },
260 | "main.JSONParams": {
261 | "type": "object",
262 | "properties": {
263 | "array": {
264 | "description": "这是一个字符串数组",
265 | "type": "array",
266 | "items": {
267 | "type": "string"
268 | }
269 | },
270 | "int": {
271 | "description": "这是一个数字",
272 | "type": "integer"
273 | },
274 | "str": {
275 | "description": "这是一个字符串",
276 | "type": "string"
277 | },
278 | "struct": {
279 | "description": "这是一个结构",
280 | "type": "object",
281 | "properties": {
282 | "field": {
283 | "type": "string"
284 | }
285 | }
286 | }
287 | }
288 | }
289 | },
290 | "tags": [
291 | {
292 | "name": "TestTag1",
293 | "externalDocs": {
294 | "description": "This is my blog site",
295 | "url": "https://razeen.me"
296 | }
297 | }
298 | ],
299 | "x-example-key": {
300 | "key": "value"
301 | }
302 | }
--------------------------------------------------------------------------------
/swaggo-gin/docs/docs.go:
--------------------------------------------------------------------------------
1 | // Package docs GENERATED BY SWAG; DO NOT EDIT
2 | // This file was generated by swaggo/swag
3 | package docs
4 |
5 | import "github.com/swaggo/swag"
6 |
7 | const docTemplate = `{
8 | "schemes": {{ marshal .Schemes }},
9 | "swagger": "2.0",
10 | "info": {
11 | "description": "{{escape .Description}}",
12 | "title": "{{.Title}}",
13 | "termsOfService": "https://razeen.me",
14 | "contact": {
15 | "name": "Razeen",
16 | "url": "https://razeen.me",
17 | "email": "me@razeen.me"
18 | },
19 | "license": {
20 | "name": "Apache 2.0",
21 | "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
22 | },
23 | "version": "{{.Version}}"
24 | },
25 | "host": "{{.Host}}",
26 | "basePath": "{{.BasePath}}",
27 | "paths": {
28 | "/file/{id}": {
29 | "get": {
30 | "description": "获取文件",
31 | "consumes": [
32 | "multipart/form-data"
33 | ],
34 | "produces": [
35 | "application/octet-stream"
36 | ],
37 | "tags": [
38 | "文件处理"
39 | ],
40 | "summary": "获取某个文件",
41 | "parameters": [
42 | {
43 | "type": "integer",
44 | "description": "文件ID",
45 | "name": "id",
46 | "in": "path",
47 | "required": true
48 | }
49 | ],
50 | "responses": {
51 | "200": {
52 | "description": "OK",
53 | "schema": {
54 | "type": "string"
55 | }
56 | }
57 | }
58 | }
59 | },
60 | "/hello": {
61 | "get": {
62 | "description": "向你说Hello",
63 | "consumes": [
64 | "multipart/form-data"
65 | ],
66 | "produces": [
67 | "application/json"
68 | ],
69 | "tags": [
70 | "测试"
71 | ],
72 | "summary": "测试SayHello",
73 | "parameters": [
74 | {
75 | "type": "string",
76 | "description": "人名",
77 | "name": "who",
78 | "in": "query",
79 | "required": true
80 | }
81 | ],
82 | "responses": {
83 | "200": {
84 | "description": "{\"msg\": \"hello Razeen\"}",
85 | "schema": {
86 | "type": "string"
87 | }
88 | },
89 | "400": {
90 | "description": "{\"msg\": \"who are you\"}",
91 | "schema": {
92 | "type": "string"
93 | }
94 | }
95 | }
96 | }
97 | },
98 | "/json": {
99 | "post": {
100 | "description": "获取JSON的示例",
101 | "consumes": [
102 | "application/json"
103 | ],
104 | "produces": [
105 | "application/json"
106 | ],
107 | "tags": [
108 | "JSON"
109 | ],
110 | "summary": "获取JSON的示例",
111 | "parameters": [
112 | {
113 | "description": "需要上传的JSON",
114 | "name": "param",
115 | "in": "body",
116 | "required": true,
117 | "schema": {
118 | "$ref": "#/definitions/main.JSONParams"
119 | }
120 | }
121 | ],
122 | "responses": {
123 | "200": {
124 | "description": "返回",
125 | "schema": {
126 | "$ref": "#/definitions/main.JSONParams"
127 | }
128 | }
129 | }
130 | }
131 | },
132 | "/list": {
133 | "get": {
134 | "description": "文件列表",
135 | "consumes": [
136 | "multipart/form-data"
137 | ],
138 | "produces": [
139 | "application/json"
140 | ],
141 | "tags": [
142 | "文件处理"
143 | ],
144 | "summary": "查看文件列表",
145 | "responses": {
146 | "200": {
147 | "description": "OK",
148 | "schema": {
149 | "$ref": "#/definitions/main.Files"
150 | }
151 | }
152 | }
153 | }
154 | },
155 | "/login": {
156 | "post": {
157 | "description": "登入",
158 | "consumes": [
159 | "multipart/form-data"
160 | ],
161 | "produces": [
162 | "application/json"
163 | ],
164 | "tags": [
165 | "登陆"
166 | ],
167 | "summary": "登陆",
168 | "parameters": [
169 | {
170 | "type": "string",
171 | "default": "admin",
172 | "description": "用户名",
173 | "name": "user",
174 | "in": "formData",
175 | "required": true
176 | },
177 | {
178 | "type": "string",
179 | "description": "密码",
180 | "name": "password",
181 | "in": "formData",
182 | "required": true
183 | }
184 | ],
185 | "responses": {
186 | "200": {
187 | "description": "{\"msg\":\"login success\"}",
188 | "schema": {
189 | "type": "string"
190 | }
191 | },
192 | "400": {
193 | "description": "{\"msg\": \"user or password error\"}",
194 | "schema": {
195 | "type": "string"
196 | }
197 | }
198 | }
199 | }
200 | },
201 | "/upload": {
202 | "post": {
203 | "description": "上传文件",
204 | "consumes": [
205 | "multipart/form-data"
206 | ],
207 | "produces": [
208 | "application/json"
209 | ],
210 | "tags": [
211 | "文件处理"
212 | ],
213 | "summary": "上传文件",
214 | "parameters": [
215 | {
216 | "type": "file",
217 | "description": "文件",
218 | "name": "file",
219 | "in": "formData",
220 | "required": true
221 | }
222 | ],
223 | "responses": {
224 | "200": {
225 | "description": "OK",
226 | "schema": {
227 | "$ref": "#/definitions/main.File"
228 | }
229 | }
230 | }
231 | }
232 | }
233 | },
234 | "definitions": {
235 | "main.File": {
236 | "type": "object",
237 | "properties": {
238 | "id": {
239 | "type": "integer"
240 | },
241 | "len": {
242 | "type": "integer"
243 | },
244 | "name": {
245 | "type": "string"
246 | }
247 | }
248 | },
249 | "main.Files": {
250 | "type": "object",
251 | "properties": {
252 | "files": {
253 | "type": "array",
254 | "items": {
255 | "$ref": "#/definitions/main.File"
256 | }
257 | },
258 | "len": {
259 | "type": "integer"
260 | }
261 | }
262 | },
263 | "main.JSONParams": {
264 | "type": "object",
265 | "properties": {
266 | "array": {
267 | "description": "这是一个字符串数组",
268 | "type": "array",
269 | "items": {
270 | "type": "string"
271 | }
272 | },
273 | "int": {
274 | "description": "这是一个数字",
275 | "type": "integer"
276 | },
277 | "str": {
278 | "description": "这是一个字符串",
279 | "type": "string"
280 | },
281 | "struct": {
282 | "description": "这是一个结构",
283 | "type": "object",
284 | "properties": {
285 | "field": {
286 | "type": "string"
287 | }
288 | }
289 | }
290 | }
291 | }
292 | },
293 | "tags": [
294 | {
295 | "name": "TestTag1",
296 | "externalDocs": {
297 | "description": "This is my blog site",
298 | "url": "https://razeen.me"
299 | }
300 | }
301 | ],
302 | "x-example-key": {
303 | "key": "value"
304 | }
305 | }`
306 |
307 | // SwaggerInfo holds exported Swagger Info so clients can modify it
308 | var SwaggerInfo = &swag.Spec{
309 | Version: "1.0",
310 | Host: "127.0.0.1:8080",
311 | BasePath: "/api/v1",
312 | Schemes: []string{"http", "https"},
313 | Title: "Swagger Example API",
314 | Description: "# Test Example Makedown\n\n### 关于使用说明\n\n吧啦吧啦吧啦\n\n",
315 | InfoInstanceName: "swagger",
316 | SwaggerTemplate: docTemplate,
317 | }
318 |
319 | func init() {
320 | swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
321 | }
322 |
--------------------------------------------------------------------------------
/gin/go.sum:
--------------------------------------------------------------------------------
1 | github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff h1:RmdPFa+slIr4SCBg4st/l/vZWVe9QJKMXGO60Bxbe04=
2 | github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw=
3 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
4 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
6 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
7 | github.com/gin-contrib/size v0.0.0-20220501091047-44dc10afe2e0 h1:sIBZ780MIR5kGBTF40N21lAPDE+dqZbcQhcvJh1rbus=
8 | github.com/gin-contrib/size v0.0.0-20220501091047-44dc10afe2e0/go.mod h1:TNclj+D/YdCIzSgy+uog3TrPw30GpZjgcF/LXS3qpoA=
9 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
10 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
11 | github.com/gin-gonic/contrib v0.0.0-20201101042839-6a891bf89f19 h1:J2LPEOcQmWaooBnBtUDV9KHFEnP5LYTZY03GiQ0oQBw=
12 | github.com/gin-gonic/contrib v0.0.0-20201101042839-6a891bf89f19/go.mod h1:iqneQ2Df3omzIVTkIfn7c1acsVnMGiSLn4XF5Blh3Yg=
13 | github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
14 | github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
15 | github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
16 | github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
17 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
18 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
19 | github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
20 | github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
21 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
22 | github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
23 | github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
24 | github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
25 | github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0=
26 | github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
27 | github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
28 | github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
29 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
30 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
31 | github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
32 | github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
33 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
34 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
35 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
36 | github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
37 | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
38 | github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
39 | github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
40 | github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
41 | github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
42 | github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
43 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
44 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
45 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
46 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
47 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
48 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
49 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
50 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
51 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
52 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
53 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
54 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
55 | github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
56 | github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
57 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
58 | github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
59 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
60 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
61 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
62 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
63 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
64 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
65 | github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU=
66 | github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
67 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
68 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
69 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
70 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
71 | github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
72 | github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
73 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
74 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
75 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
76 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
77 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
78 | github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
79 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
80 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
81 | github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
82 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
83 | github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
84 | github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
85 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
86 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
87 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
88 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
89 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
90 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
91 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
92 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
93 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
94 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
95 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
96 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
97 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
98 | golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
99 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
100 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
101 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
102 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
103 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
104 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
105 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
106 | golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
107 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
108 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
109 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
110 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
111 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
112 | google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
113 | google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
114 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
115 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
116 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
117 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
118 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
119 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
120 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
121 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
122 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
123 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
124 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
125 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
126 |
--------------------------------------------------------------------------------
/gomod/README.md:
--------------------------------------------------------------------------------
1 | # [Go学习笔记(十)老项目迁移 go module 大型灾难记录](https://razeencheng.com/post/accidents-of-migrating-to-go-modules.html)
2 |
3 |
4 |
5 | 最近在改造一个比较早期的一个项目,其中就涉及到用将原来 `Vendor` 管理依赖换成 `Go Modules` 来管理。 然而过程真是一波三折,在这里总结一下此次 `Go Modules` 改造中遇到的问题,以及解决方法。
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | ### 背景
14 |
15 | - go version:
16 |
17 | ```bash
18 | $ go version
19 | go version go1.16.5 darwin/amd64
20 | ```
21 |
22 | - 简化的 demo 如下, 很 “简单” 我们只要把 `hello world` 输出即可。
23 |
24 | ``` go
25 | package main
26 |
27 | import (
28 | "github.com/coreos/etcd/pkg/transport"
29 | "github.com/google/certificate-transparency-go/tls"
30 | "github.com/qiniu/api.v7/auth/qbox"
31 | "go.etcd.io/etcd/clientv3"
32 | "google.golang.org/grpc"
33 | "qiniupkg.com/x/log.v7"
34 | )
35 |
36 | func main() {
37 |
38 | _ = transport.TLSInfo{}
39 |
40 | _ = clientv3.WatchResponse{}
41 |
42 | _, _ = clientv3.New(clientv3.Config{})
43 |
44 | _ = qbox.NewMac("", "")
45 |
46 | _ = tls.DigitallySigned{}
47 |
48 | _ = grpc.ClientConn{}
49 |
50 | log.Info("hello world")
51 | }
52 | ```
53 |
54 |
55 |
56 | ### 实战
57 |
58 | 直接初始化,并 tidy 一下。
59 |
60 | ```bash
61 | $ go mod init demo-go/gomod
62 | go: creating new go.mod: module demo-go/gomod
63 | go: to add module requirements and sums:
64 | go mod tidy
65 |
66 | $ go mod tidy
67 | go: finding module for ...
68 | demo-go/gomod imports
69 | qiniupkg.com/x/log.v7: module qiniupkg.com/x@latest found (v1.11.5), but does not contain package qiniupkg.com/x/log.v7
70 | demo-go/gomod imports
71 | github.com/qiniu/api.v7/auth/qbox imports
72 | github.com/qiniu/x/bytes.v7/seekable: module github.com/qiniu/x@latest found (v1.11.5), but does not contain package github.com/qiniu/x/bytes.v7/seekable
73 | demo-go/gomod imports
74 | go.etcd.io/etcd/clientv3 imports
75 | github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context: package github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context provided by github.com/coreos/etcd at latest version v2.3.8+incompatible but not at required version v3.3.10+incompatible
76 | demo-go/gomod imports
77 | go.etcd.io/etcd/clientv3 imports
78 | github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc: package github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc provided by github.com/coreos/etcd at latest version v2.3.8+incompatible but not at required version v3.3.10+incompatible
79 | demo-go/gomod imports
80 | go.etcd.io/etcd/clientv3 imports
81 | github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/credentials: package github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/credentials provided by github.com/coreos/etcd at latest version v2.3.8+incompatible but not at required version v3.3.10+incompatible
82 | demo-go/gomod imports
83 | go.etcd.io/etcd/clientv3 imports
84 | github.com/coreos/etcd/storage/storagepb: package github.com/coreos/etcd/storage/storagepb provided by github.com/coreos/etcd at latest version v2.3.8+incompatible but not at required version v3.3.10+incompatible
85 | ```
86 |
87 |
88 |
89 | 好家伙,报错了。我们先看到前两行
90 |
91 | 1. `qiniupkg.com/x@latest` 中没有 `qiniupkg.com/x/log.v7`;
92 | 2. `github.com/qiniu/x@latest` 中没有 `github.com/qiniu/x/bytes.v7/seekable`;
93 |
94 | 这看起来应该是一个问题, `qiniupkg.com/x` 和`github.com/qiniu/x` 应该是同一个包,不同镜像。于是我到 Github 看一下 `@lastet` 版本的代码,确实没有`bytes.v7` 包了。人肉查找,最后在 `v1.7.8` 版本,我们找到了 `bytes.v7` 包。
95 |
96 |
97 |
98 | 于是,我们可以指定一下版本。
99 |
100 | ```bash
101 | go mod edit -replace qiniupkg.com/x=qiniupkg.com/x@v1.7.8
102 | go mod edit -replace github.com/qiniu/x=github.com/qiniu/x@v1.7.8
103 | ```
104 |
105 |
106 |
107 | 继续往下看,接下来的几个问题是一类的,都是`etcd`导致的。
108 |
109 | 意思是 `go.etcd.io/etcd/clientv3` 导入了 `github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context`, 同时 `github.com/coreos/etcd@v2.3.8` 中 提供了 `github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context` 。 但是,我们这里需要 `github.com/coreos/etcd@v3.3.10`, 而该版本并不提供 `github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context` 。
110 |
111 | 我们直接更新 etcd 到的 `v3.3.10` 试试。
112 |
113 | ```bash
114 | go mod edit -replace go.etcd.io/etcd=go.etcd.io/etcd@v3.3.20+incompatible
115 | ```
116 |
117 |
118 |
119 |
120 |
121 | 我们再 ` go mod tidy` 下。
122 |
123 | ```bash
124 | $ go mod tidy
125 | go: demo-go/gomod imports
126 | go.etcd.io/etcd/clientv3 tested by
127 | go.etcd.io/etcd/clientv3.test imports
128 | github.com/coreos/etcd/auth imports
129 | github.com/coreos/etcd/mvcc/backend imports
130 | github.com/coreos/bbolt: github.com/coreos/bbolt@v1.3.6: parsing go.mod:
131 | module declares its path as: go.etcd.io/bbolt
132 | but was required as: github.com/coreos/bbolt
133 | ```
134 |
135 | 这个错误和鸟窝这篇 [Etcd使用go module的灾难](https://colobu.com/2020/04/09/accidents-of-etcd-and-go-module/)一致,`go.etcd.io/bbolt` 和 `github.com/coreos/bbolt` 包名不一致,我们替换一下。
136 |
137 | ``` bash
138 | go mod edit -replace github.com/coreos/bbolt@v1.3.6=go.etcd.io/bbolt@v1.3.6
139 | ```
140 |
141 |
142 |
143 |
144 |
145 | 继续,`go mod tidy` 一下。
146 |
147 | ```bash
148 | $ go mod tidy
149 | ...
150 | demo-go/gomod imports
151 | go.etcd.io/etcd/clientv3 imports
152 | github.com/coreos/etcd/clientv3/balancer: module github.com/coreos/etcd@latest found (v2.3.8+incompatible), but does not contain package github.com/coreos/etcd/clientv3/balancer
153 | demo-go/gomod imports
154 | go.etcd.io/etcd/clientv3 imports
155 | github.com/coreos/etcd/clientv3/balancer/picker: module github.com/coreos/etcd@latest found (v2.3.8+incompatible), but does not contain package github.com/coreos/etcd/clientv3/balancer/picker
156 | demo-go/gomod imports
157 | go.etcd.io/etcd/clientv3 imports
158 | github.com/coreos/etcd/clientv3/balancer/resolver/endpoint: module github.com/coreos/etcd@latest found (v2.3.8+incompatible), but does not contain package github.com/coreos/etcd/clientv3/balancer/resolver/endpoint
159 | demo-go/gomod imports
160 | go.etcd.io/etcd/clientv3 imports
161 | github.com/coreos/etcd/clientv3/credentials: module github.com/coreos/etcd@latest found (v2.3.8+incompatible), but does not contain package github.com/coreos/etcd/clientv3/credentials
162 | demo-go/gomod imports
163 | go.etcd.io/etcd/clientv3 tested by
164 | go.etcd.io/etcd/clientv3.test imports
165 | github.com/coreos/etcd/integration imports
166 | github.com/coreos/etcd/proxy/grpcproxy imports
167 | google.golang.org/grpc/naming: module google.golang.org/grpc@latest found (v1.39.0), but does not contain package google.golang.org/grpc/naming
168 | ```
169 |
170 | 好家伙,又是`etcd`。 仔细一看,我们导入了`github.com/coreos/etcd` 和 `go.etcd.io/etcd` 两个版本`etcd`, 我们前面只替换了一个。现在我们把另外一个也替换了。
171 |
172 | ```bash
173 | go mod edit -replace github.com/coreos/etcd=github.com/coreos/etcd@v3.3.20+incompatible
174 | ```
175 |
176 |
177 |
178 |
179 |
180 | 再`go mod tidy`下,这个错误没有了,但还有个`grpc`的错误,继续找原因。原来是` google.golang.org/grpc` `v1.39.0` 版本没有` google.golang.org/grpc/naming` 包。 上 Github 仓库, 找了一下历史版本,`v1.29.1`上是有这个包的,我们继续替换。
181 |
182 | ```bash
183 | go mod edit -replace google.golang.org/grpc=google.golang.org/grpc@v1.29.1
184 | ```
185 |
186 |
187 |
188 |
189 |
190 | 这下,终于,`go mod tidy`通过了,可以开心的输出`hello world` 了。
191 |
192 |
193 |
194 |
195 |
196 | 然而,
197 |
198 | ``` bash
199 | $ go run main.go
200 | # github.com/coreos/etcd/clientv3/balancer/resolver/endpoint
201 | ../../../go/pkg/mod/github.com/coreos/etcd@v3.3.20+incompatible/clientv3/balancer/resolver/endpoint/endpoint.go:114:78: undefined: resolver.BuildOption
202 | ../../../go/pkg/mod/github.com/coreos/etcd@v3.3.20+incompatible/clientv3/balancer/resolver/endpoint/endpoint.go:182:31: undefined: resolver.ResolveNowOption
203 | # github.com/coreos/etcd/clientv3/balancer/picker
204 | ../../../go/pkg/mod/github.com/coreos/etcd@v3.3.20+incompatible/clientv3/balancer/picker/err.go:37:44: undefined: balancer.PickOptions
205 | ../../../go/pkg/mod/github.com/coreos/etcd@v3.3.20+incompatible/clientv3/balancer/picker/roundrobin_balanced.go:55:54: undefined: balancer.PickOptions
206 | ```
207 |
208 | 意不意外,惊不惊喜!!
209 |
210 | 原来`etcd`包依赖了`grpc`的`resolver`包,但我导入的`v1.29.1`版本的`grpc`是没有这个包的。到 `grpc`[仓库](https://github.com/grpc/grpc-go/blob/v1.26.0/resolver/resolver.go) 挨个版本看了一下,确实只有`v1.26.0`版本才声明了`type BuildOption` 。于是,我们再次使用替换大法。
211 |
212 | ```bash
213 | go mod edit -replace google.golang.org/grpc=google.golang.org/grpc@v1.26.0
214 | ```
215 |
216 |
217 |
218 | 再次`tidy`, 运行! 终于,看到了久违的`hello world!`
219 |
220 | ```bash
221 | $ go run main.go
222 | 2021/07/20 12:27:09.642431 [INFO] /Users/razeen/wspace/github/demo-go/gomod/main.go:26: hello world
223 | ```
224 |
225 |
226 |
227 |
228 |
229 | ### 总结
230 |
231 | #### 项目规范
232 |
233 | 现在我们回过头看下这个 demo 项目,其实很有问题。
234 |
235 | ``` go
236 | "github.com/coreos/etcd/pkg/transport"
237 | "github.com/google/certificate-transparency-go/tls"
238 | "github.com/qiniu/api.v7/auth/qbox"
239 | "go.etcd.io/etcd/clientv3"
240 | "google.golang.org/grpc"
241 | "qiniupkg.com/x/log.v7"
242 | ```
243 |
244 | `etcd` 和 `qiniupkg`的包完全可以统一,只导入一种!而且,后来我们发现`log.v7`这包也是意外导入的....
245 |
246 | 这也是在改造我们一些老的项目时遇到的问题,以前用`vendor` `go get` 没有注意到这些问题,这是需要提前规范的。
247 |
248 |
249 |
250 | #### 看懂 `go.mod`
251 |
252 | 我们来简单看一下,经历各种坎坷后,得出的`go.mod` 文件。
253 |
254 | ``` go
255 | module demo-go/gomod
256 |
257 | go 1.16
258 |
259 | replace qiniupkg.com/x => qiniupkg.com/x v1.7.8
260 |
261 | replace github.com/qiniu/x => github.com/qiniu/x v1.7.8
262 |
263 | replace go.etcd.io/etcd => go.etcd.io/etcd v3.3.20+incompatible
264 |
265 | replace github.com/coreos/bbolt v1.3.6 => go.etcd.io/bbolt v1.3.6
266 |
267 | replace github.com/coreos/etcd => github.com/coreos/etcd v3.3.20+incompatible
268 |
269 | replace google.golang.org/grpc => google.golang.org/grpc v1.26.0
270 |
271 | require (
272 | github.com/coreos/bbolt v1.3.6 // indirect
273 | github.com/coreos/etcd v3.3.10+incompatible
274 | github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
275 | github.com/google/certificate-transparency-go v1.1.1
276 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
277 | github.com/qiniu/api.v7 v7.2.5+incompatible
278 | github.com/qiniu/x v0.0.0-00010101000000-000000000000 // indirect
279 | github.com/soheilhy/cmux v0.1.5 // indirect
280 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
281 | go.etcd.io/etcd v0.0.0-20200513171258-e048e166ab9c
282 | google.golang.org/grpc v1.29.1
283 | qiniupkg.com/x v0.0.0-00010101000000-000000000000
284 | sigs.k8s.io/yaml v1.2.0 // indirect
285 | )
286 | ```
287 |
288 |
289 |
290 | 我们先看一个常见的这几个[指令](https://golang.org/ref/mod#go-mod-file-module),
291 |
292 | - `module` 定义主模块的路径;
293 | - `go` 编写该`mod`文件时的go版本;
294 | - `require` 声明给定模块依赖项的最低要求版本;
295 | - `replace` 手动指定的依赖模块 (可以替换全部的版本、指定的版本、本地的版本等等 );
296 |
297 |
298 |
299 | 还有就是 `v3.3.20+incompatible` 后面的 `+incompatible` , 这是指兼容的版本,指依赖库的版本是`v2` 或以上,但`go.mod`和 依赖库路径 没有按照官方指定的方式命名,会加上这个。
300 |
301 |
302 |
303 | `v0.0.0-00010101000000-000000000000` 这是一个伪版本,在和 不兼容 module 或 标记的版本不可用的时候,回打上这个伪版本。
304 |
305 |
306 |
307 | `// indirect` 这指明这些不是我们直接引用的依赖。
308 |
309 |
310 |
311 | 除此之外,以下指令也可了解一下。
312 |
313 | ``` bash
314 | # 查看当前模块以及所有的依赖模块
315 | go list -m all
316 |
317 | # 查看某个模块的以及打标签的版本
318 | go list -m -versions go.etcd.io/etcd
319 |
320 | # 升级特定的包
321 | go get xx@version 升级特定的包
322 |
323 | # 了解为什么需要模块
324 | go mod why -m all
325 |
326 | # 为什么需要指定(google.golang.org/grpc)的模块
327 | go mod why -m google.golang.org/grpc
328 | ```
329 |
330 |
331 |
332 | 更多可以细读[官方文档](https://golang.org/ref/mod#incompatible-versions),感谢阅读。
333 |
334 |
335 |
336 |
337 |
338 | ### 参考
339 |
340 | - [Using Go Modules](https://blog.golang.org/using-go-modules)
341 | - [Minimal Version Selection](https://research.swtch.com/vgo-mvs)
342 | - [跳出Go module的泥潭](https://colobu.com/2018/08/27/learn-go-module/)
343 | - [Etcd使用go module的灾难](https://colobu.com/2020/04/09/accidents-of-etcd-and-go-module/)
344 | - [浅谈Go Modules原理](https://duyanghao.github.io/golang-module/)
345 |
346 |
--------------------------------------------------------------------------------
/os-exec/README.md:
--------------------------------------------------------------------------------
1 | [#Go学习笔记(八) | 使用 os/exec 执行命令](https://razeencheng.com/post/simple-use-go-exec-command.html)
2 |
3 | 用Go去调用一些外部的命令其实很愉快的,这遍文章就总结一下我自己日常用的比较多的几种方法。
4 |
5 |
6 |
7 | ### 关于Unix标准输入输出
8 |
9 | 在具体聊`os/exec`的使用前,了解一下shell的标准输出是很有必要的。
10 |
11 | 我们平常会用到或看到这样的命令:
12 |
13 | ```shell
14 | $ ls xxx 1>out.txt 2>&1
15 | $ nohup xxx 2>&1 &
16 | ```
17 |
18 | 你知道这里`1,2`含义么?
19 |
20 |
21 |
22 | 其实这里的`1,2`指的就是Unix文件描述符。文件描述符其实就一数字,每一个文件描述符代表的都是一个文件。如果你打开100个文件,你就会获取到100个文件描述符。
23 |
24 |
25 |
26 | 这里需要注意的一点就是,在Unix中[**一切皆文件**]()。当然,这里我们不必去深究,我们需要知道的是`1,2`代表的是标准输出`stdout`与标准错误输出`stderr`。还有`0`代表标准输入`stdin`。
27 |
28 |
29 |
30 | 在`os/exec`中就用到了`Stdin`,`Stdout`,`Stderr`,这些基本Unix知识或能帮助我们更好理解这些参数。
31 |
32 |
33 |
34 | ### os/exec
35 |
36 | `os/exec`包内容并不多,我们大概过一下。
37 |
38 | 1. [LookPath(file string) (string, error)](https://godoc.org/os/exec#LookPath)
39 |
40 | 寻找可执行文件路径,如果你指定的可执行文件在`$PATH`中,就会返回这个可执行文件的相对/绝对路径;如果你指定的是一个文件路径,他就是去判断文件是否可读取/执行,返回的是一样的路径。
41 |
42 | 在我们需要使用一些外部命令/可执行文件的时候,我们可以先使用该函数判断一下该命令/可执行文件是否有效。
43 |
44 | 2. [Command(name string, arg ...string) *Cmd](https://godoc.org/os/exec#Command)
45 |
46 | 使用你输入的参数,返回Cmd指针,可用于执行Cmd的方法。
47 |
48 | 这里`name`就是我们的命令/可执行文件,后面的参数可以一个一个输入。
49 |
50 | 3. [CommandContext(ctx context.Context, name string, arg ...string) *Cmd](https://godoc.org/os/exec#CommandContext)
51 |
52 | 和上面功能一样,不过我们可以用上下文做一些超时等控制。
53 |
54 | 4. 之后几个就是Cmd的一些方法。
55 |
56 | - [(c *Cmd) CombinedOutput() ([]byte, error)](https://godoc.org/os/exec#Cmd.CombinedOutput) 将标准输出,错误输出一起返回;
57 |
58 | - [(c *Cmd) Output() ([]byte, error)](https://godoc.org/os/exec#Cmd.Output) 输出标准输出,错误从error返回;
59 |
60 | - [(c *Cmd) Run() error](https://godoc.org/os/exec#Cmd.Run) 执行任务,等待执行完成;
61 |
62 | - [(c *Cmd) Start() error](https://godoc.org/os/exec#Cmd.Start), [(c *Cmd) Wait() error](https://godoc.org/os/exec#Cmd.Wait) 前者执行任务,不等待完成,用后者等待,并释放资源;
63 |
64 | - [(c *Cmd) StderrPipe() (io.ReadCloser, error)](https://godoc.org/os/exec#Cmd.StderrPipe)
65 |
66 | [(c *Cmd) StdinPipe() (io.WriteCloser, error)](https://godoc.org/os/exec#Cmd.StdinPipe)
67 |
68 | [(c *Cmd) StdoutPipe() (io.ReadCloser, error)](https://godoc.org/os/exec#Cmd.StdoutPipe)
69 |
70 | 这三个功能类似,就是提供一个标准输入/输出/错误输出的管道,我们可用这些管道中去输入输出。
71 |
72 | 其实读完,结合官方的一些example,使用很简单,下面具体写几个场景。
73 |
74 |
75 |
76 | *注*
77 |
78 | 1. 本文全部的Demo在[这里](https://github.com/razeencheng/demo-go/tree/master/os-exec)。
79 |
80 | 2. `./testcmd/testcmd`是我用Go写的一个简单的可执行文件,可以根据指定的参数 输出/延时输出/输出错误,方便我们演示。如下
81 |
82 | ```go
83 | func main() {
84 |
85 | var (
86 | start bool
87 | e bool
88 | )
89 |
90 | flag.BoolVar(&start, "s", false, "start output")
91 | flag.BoolVar(&e, "e", false, "output err")
92 | flag.Parse()
93 |
94 | if start {
95 | for i := 5; i > 0; i-- {
96 | fmt.Fprintln(os.Stdout, "test cmd output", i)
97 | time.Sleep(1 * time.Second)
98 | }
99 | }
100 |
101 | if e {
102 | fmt.Fprintln(os.Stderr, "a err occur")
103 | os.Exit(1)
104 | }
105 |
106 | fmt.Fprintln(os.Stdout, "test cmd stdout")
107 | }
108 | ```
109 |
110 |
111 |
112 | ### 简单执行
113 |
114 | ```go
115 | // 简单执行
116 | func test1() {
117 | cmd := exec.Command("./testcmd/testcmd", "-s")
118 |
119 | // 使用CombinedOutput 将stdout stderr合并输出
120 | out, err := cmd.CombinedOutput()
121 | if err != nil {
122 | log.Printf("test1 failed %s\n", err)
123 | }
124 | log.Println("test1 output ", string(out))
125 | }
126 | ```
127 |
128 | 输出:
129 |
130 | ```shell
131 | Run Test 1
132 | 2019/06/06 18:02:39 test1 output test cmd output 5
133 | test cmd output 4
134 | test cmd output 3
135 | test cmd output 2
136 | test cmd output 1
137 | done
138 | ```
139 |
140 | 整个过程等待5秒,所有结果一次输出。
141 |
142 |
143 |
144 | ### 分离标准输出与错误输出
145 |
146 | 将错误分开输出,同时开了两个协成,同步的接收命令的输出内容。
147 |
148 | ```go
149 | // stdout & stderr 分开输出
150 | func test2() {
151 | cmd := exec.Command("./testcmd/testcmd", "-s", "-e")
152 | stdout, _ := cmd.StdoutPipe()
153 | stderr, _ := cmd.StderrPipe()
154 | cmd.Start()
155 |
156 | go func() {
157 | for {
158 | buf := make([]byte, 1024)
159 | n, err := stderr.Read(buf)
160 |
161 | if n > 0 {
162 | log.Printf("read err %s", string(buf[:n]))
163 | }
164 |
165 | if n == 0 {
166 | break
167 | }
168 |
169 | if err != nil {
170 | log.Printf("read err %v", err)
171 | return
172 | }
173 | }
174 | }()
175 |
176 | go func() {
177 | for {
178 | buf := make([]byte, 1024)
179 | n, err := stdout.Read(buf)
180 |
181 | if n == 0 {
182 | break
183 | }
184 |
185 | if n > 0 {
186 | log.Printf("read out %s", string(buf[:n]))
187 |
188 | }
189 |
190 | if n == 0 {
191 | break
192 | }
193 |
194 | if err != nil {
195 | log.Printf("read out %v", err)
196 | return
197 | }
198 |
199 | }
200 | }()
201 |
202 | err := cmd.Wait()
203 | if err != nil {
204 | log.Printf("cmd wait %v", err)
205 | return
206 | }
207 | }
208 | ```
209 |
210 | 输出:
211 |
212 | ```shell
213 | Run Test 2
214 | 2019/06/06 18:02:39 read out test cmd output 5
215 | 2019/06/06 18:02:40 read out test cmd output 4
216 | 2019/06/06 18:02:41 read out test cmd output 3
217 | 2019/06/06 18:02:42 read out test cmd output 2
218 | 2019/06/06 18:02:43 read out test cmd output 1
219 | 2019/06/06 18:02:44 read err a err occur
220 | 2019/06/06 18:02:44 cmd wait exit status 1
221 | ```
222 |
223 |
224 |
225 | ### 按行读取输出内容
226 |
227 | 使用`bufio`按行读取输出内容。
228 |
229 | ``` go
230 | // 按行读输出的内容
231 | func test3() {
232 | cmd := exec.Command("./testcmd/testcmd", "-s", "-e")
233 | stdout, _ := cmd.StdoutPipe()
234 | stderr, _ := cmd.StderrPipe()
235 | oReader := bufio.NewReader(stdout)
236 | eReader := bufio.NewReader(stderr)
237 |
238 | cmd.Start()
239 |
240 | go func() {
241 | for {
242 | line, err := oReader.ReadString('\n')
243 |
244 | if line != "" {
245 | log.Printf("read line %s", line)
246 | }
247 |
248 | if err != nil || line == "" {
249 | log.Printf("read line err %v", err)
250 | return
251 | }
252 |
253 | }
254 | }()
255 |
256 | go func() {
257 | for {
258 | line, err := eReader.ReadString('\n')
259 |
260 | if line != "" {
261 | log.Printf("read err %s", line)
262 | }
263 |
264 | if err != nil || line == "" {
265 | log.Printf("read err %v", err)
266 | return
267 | }
268 |
269 | }
270 | }()
271 |
272 | err := cmd.Wait()
273 | if err != nil {
274 | log.Printf("cmd wait %v", err)
275 | return
276 | }
277 | }
278 | ```
279 |
280 | 输出:
281 |
282 | ``` shell
283 | Run Test 3
284 | 2019/06/06 18:06:44 read line test cmd output 5
285 | 2019/06/06 18:06:45 read line test cmd output 4
286 | 2019/06/06 18:06:46 read line test cmd output 3
287 | 2019/06/06 18:06:47 read line test cmd output 2
288 | 2019/06/06 18:06:48 read line test cmd output 1
289 | 2019/06/06 18:06:49 read err a err occur
290 | 2019/06/06 18:06:49 cmd wait exit status 1
291 | ```
292 |
293 |
294 |
295 |
296 |
297 | ### 设置执行超时时间
298 |
299 | 有时候我们要控制命令的执行时间,这是就可以使用上下文去控制了。
300 |
301 | ``` go
302 | // 通过上下文控制超时
303 | func test4() {
304 |
305 | ctx, calcel := context.WithTimeout(context.Background(), 2*time.Second)
306 | defer calcel()
307 |
308 | cmd := exec.CommandContext(ctx, "./testcmd/testcmd", "-s", "-e")
309 |
310 | stdout, _ := cmd.StdoutPipe()
311 | stderr, _ := cmd.StderrPipe()
312 | oReader := bufio.NewReader(stdout)
313 | eReader := bufio.NewReader(stderr)
314 |
315 | cmd.Start()
316 |
317 | go func() {
318 | for {
319 | line, err := oReader.ReadString('\n')
320 |
321 | if line != "" {
322 | log.Printf("read line %s", line)
323 | }
324 |
325 | if err != nil || line == "" {
326 | log.Printf("read line err %v", err)
327 | return
328 | }
329 |
330 | }
331 | }()
332 |
333 | go func() {
334 | for {
335 | line, err := eReader.ReadString('\n')
336 |
337 | if line != "" {
338 | log.Printf("read err %s", line)
339 | }
340 |
341 | if err != nil || line == "" {
342 | log.Printf("read err %v", err)
343 | return
344 | }
345 |
346 | }
347 | }()
348 |
349 | err := cmd.Wait()
350 | if err != nil {
351 | log.Printf("cmd wait %v", err)
352 | return
353 | }
354 | }
355 | ```
356 |
357 | 输出:
358 |
359 | ```shell
360 | Run Test 4
361 | 2019/06/06 18:06:49 read line err EOF
362 | 2019/06/06 18:06:49 read err EOF
363 | 2019/06/06 18:06:49 read line test cmd output 5
364 | 2019/06/06 18:06:50 read line test cmd output 4
365 | 2019/06/06 18:06:51 read line err EOF
366 | 2019/06/06 18:06:51 read err EOF
367 | 2019/06/06 18:06:51 cmd wait signal: killed
368 | ```
369 |
370 |
371 |
372 | ### 持续输入指令,交互模式
373 |
374 | 有很多命令支持交互模式,我们进入之后就可以持续的输入一些命令,同时获取输出。如`openssl`命令。
375 |
376 | 下面我们需要进入交换模式,执行输入三个命令,并获取输出。
377 |
378 | ``` go
379 | // 持续输入
380 | func test5() {
381 | cmd := exec.Command("openssl")
382 |
383 | stdout, _ := cmd.StdoutPipe()
384 | stderr, _ := cmd.StderrPipe()
385 |
386 | stdin, _ := cmd.StdinPipe()
387 |
388 | cmd.Start()
389 |
390 | // 读
391 | var wg sync.WaitGroup
392 | wg.Add(3)
393 | go func() {
394 | defer wg.Done()
395 | for {
396 | buf := make([]byte, 1024)
397 | n, err := stderr.Read(buf)
398 |
399 | if n > 0 {
400 | fmt.Println(string(buf[:n]))
401 | }
402 |
403 | if n == 0 {
404 | break
405 | }
406 |
407 | if err != nil {
408 | log.Printf("read err %v", err)
409 | return
410 | }
411 | }
412 | }()
413 |
414 | go func() {
415 | defer wg.Done()
416 | for {
417 | buf := make([]byte, 1024)
418 | n, err := stdout.Read(buf)
419 |
420 | if n == 0 {
421 | break
422 | }
423 |
424 | if n > 0 {
425 | fmt.Println(string(buf[:n]))
426 | }
427 |
428 | if n == 0 {
429 | break
430 | }
431 |
432 | if err != nil {
433 | log.Printf("read out %v", err)
434 | return
435 | }
436 |
437 | }
438 | }()
439 |
440 | // 写
441 | go func() {
442 | stdin.Write([]byte("version\n\n"))
443 | stdin.Write([]byte("ciphers -v\n\n"))
444 | stdin.Write([]byte("s_client -connect razeencheng.com:443"))
445 | stdin.Close()
446 | wg.Done()
447 | }()
448 |
449 | wg.Wait()
450 | err := cmd.Wait()
451 | if err != nil {
452 | log.Printf("cmd wait %v", err)
453 | return
454 | }
455 |
456 | }
457 | ```
458 |
459 | 这里,我们就用到了`stdin`标准输入了。输出如下:
460 |
461 | ``` shell
462 | Run Test 5
463 | OpenSSL> LibreSSL 2.6.5
464 | OpenSSL> OpenSSL>
465 | ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(256) Mac=AEAD
466 | ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(256) Mac=AEAD
467 | ECDHE-RSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA384
468 | ECDHE-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA384
469 | ECDHE-RSA-AES256-SHA SSLv3 Kx=ECDH Au=RSA Enc=AES(256) Mac=SHA1
470 | ECDHE-ECDSA-AES256-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA1
471 | DHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=DH Au=RSA Enc=AESGCM(256) Mac=AEAD
472 | DHE-RSA-AES256-SHA256 TLSv1.2 Kx=DH Au=RSA Enc=AES(256) Mac=SHA256
473 | DES-CBC-SHA SSLv3 Kx=RSA Au=RSA Enc=DES(56) Mac=SHA1
474 | ...
475 | OpenSSL> OpenSSL>
476 | 4466583148:error:14004410:SSL routines:CONNECT_CR_SRVR_HELLO:sslv3 alert handshake failure:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.260.1/libressl-2.6/ssl/ssl_pkt.c:1205:SSL alert number 40
477 | 4466583148:error:140040E5:SSL routines:CONNECT_CR_SRVR_HELLO:ssl handshake failure:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.260.1/libressl-2.6/ssl/ssl_pkt.c:585:
478 |
479 | CONNECTED(00000005)
480 | ---
481 | no peer certificate available
482 | ---
483 | No client certificate CA names sent
484 | ---
485 | SSL handshake has read 7 bytes and written 0 bytes
486 | ---
487 | New, (NONE), Cipher is (NONE)
488 | Secure Renegotiation IS NOT supported
489 | Compression: NONE
490 | Expansion: NONE
491 | No ALPN negotiated
492 | SSL-Session:
493 | Protocol : TLSv1.2
494 | Cipher : 0000
495 | Session-ID:
496 | Session-ID-ctx:
497 | Master-Key:
498 | Start Time: 1559815613
499 | Timeout : 7200 (sec)
500 | Verify return code: 0 (ok)
501 | ---
502 |
503 | ```
504 |
505 |
506 |
507 |
508 |
509 | —END---
--------------------------------------------------------------------------------
/grpc/demo3/README.md:
--------------------------------------------------------------------------------
1 |
2 | # [gRPC在Go中的使用(三)gRPC实现TLS加密通信与流通信](https://razeen.me/post/how-to-use-grpc-in-golang-03.html)
3 |
4 | 在前面的两篇博客中,我们已经知道了如何利用gRPC建立简单RPC通信。但这样简单的实现有时候满足不了我们的业务需求。在一些场景中我们需要防止数据被劫持,或是一些场景中我们希望客户端与服务器不是简单的一问一答,而是建立起一个流式的RPC通信,那么该怎么做到呢?
5 |
6 |
7 |
8 | ### TLS加密通信
9 |
10 | TLS加密无非就是认证客户端与服务器,如果对SSL/TLS加密通信有所了解的童鞋都知道我们首先需要两张证书。
11 |
12 | 所以作为准备工作,我们首先要申请两张测试证书。一张客户端证书,一张服务器证书。
13 |
14 | #### 生成测试证书
15 |
16 | 利用[MySSL测试证书生成工具](https://myssl.com/create_test_cert.html)我们可以很简单的生成两张证书,如下所示:
17 |
18 | 如图,填入域名生成一张服务器证书,然后将私钥,证书链,根证书都下载下来,保存到文件。
19 |
20 | 
21 |
22 | 同样,生成一张客户端证书并保存。
23 |
24 | 
25 |
26 | #### 客户端与服务器TLS认证
27 |
28 | 在gRPC通信中,我们完成服务器认证与客户端认证主要使用的是grpc下的[credentials](https://godoc.org/google.golang.org/grpc/credentials)库。下面通过实例来看看怎么使用。
29 |
30 | [代码实例]()
31 |
32 | **服务端实现**
33 |
34 | ```go
35 | func main() {
36 | lis, err := net.Listen("tcp", ":8080")
37 | if err != nil {
38 | panic(err)
39 | }
40 |
41 | // 加载证书和密钥 (同时能验证证书与私钥是否匹配)
42 | cert, err := tls.LoadX509KeyPair("certs/test_server.pem", "certs/test_server.key")
43 | if err != nil {
44 | panic(err)
45 | }
46 |
47 | // 将根证书加入证书池
48 | // 测试证书的根如果不加入可信池,那么测试证书将视为不可惜,无法通过验证。
49 | certPool := x509.NewCertPool()
50 | rootBuf, err := ioutil.ReadFile("certs/root.pem")
51 | if err != nil {
52 | panic(err)
53 | }
54 |
55 | if !certPool.AppendCertsFromPEM(rootBuf) {
56 | panic("fail to append test ca")
57 | }
58 |
59 | tlsConf := &tls.Config{
60 | ClientAuth: tls.RequireAndVerifyClientCert,
61 | Certificates: []tls.Certificate{cert},
62 | ClientCAs: certPool,
63 | }
64 |
65 | serverOpt := grpc.Creds(credentials.NewTLS(tlsConf))
66 | grpcServer := grpc.NewServer(serverOpt)
67 |
68 | pb.RegisterHelloWorldServiceServer(grpcServer, &SayHelloServer{})
69 |
70 | log.Println("Server Start...")
71 | grpcServer.Serve(lis)
72 | }
73 | ```
74 |
75 | **客户端实现**
76 |
77 | ```go
78 | func main() {
79 | cert, err := tls.LoadX509KeyPair("certs/test_client.pem", "certs/test_client.key")
80 | if err != nil {
81 | panic(err)
82 | }
83 | // 将根证书加入证书池
84 | certPool := x509.NewCertPool()
85 | bs, err := ioutil.ReadFile("certs/root.pem")
86 | if err != nil {
87 | panic(err)
88 | }
89 |
90 | if !certPool.AppendCertsFromPEM(bs) {
91 | panic("fail to append test ca")
92 | }
93 |
94 | // 新建凭证
95 | // ServerName 需要与服务器证书内的通用名称一致
96 | transportCreds := credentials.NewTLS(&tls.Config{
97 | ServerName: "server.razeen.me",
98 | Certificates: []tls.Certificate{cert},
99 | RootCAs: certPool,
100 | })
101 |
102 | dialOpt := grpc.WithTransportCredentials(transportCreds)
103 |
104 | conn, err := grpc.Dial("localhost:8080", dialOpt)
105 | if err != nil {
106 | log.Fatalf("Dial failed:%v", err)
107 | }
108 | defer conn.Close()
109 |
110 | client := pb.NewHelloWorldServiceClient(conn)
111 | resp1, err := client.SayHelloWorld(context.Background(), &pb.HelloWorldRequest{
112 | Greeting: "Hello Server 1 !!",
113 | Infos: map[string]string{"hello": "world"},
114 | })
115 | if err != nil {
116 | log.Printf("%v", err)
117 | }
118 |
119 | log.Printf("Resp1:%+v", resp1)
120 |
121 | resp2, err := client.SayHelloWorld(context.Background(), &pb.HelloWorldRequest{
122 | Greeting: "Hello Server 2 !!",
123 | })
124 | if err != nil {
125 | log.Printf("%v", err)
126 | }
127 |
128 | log.Printf("Resp2:%+v", resp2)
129 | }
130 | ```
131 |
132 | 从代码中,我们不难看出,主要是创建一个通信凭证(TransportCredentials)。利用`credentials`库的`NewTLS`方法从`tls`加载一个通信凭证用于通信。而在其中需要注意的是:
133 |
134 | - 如果你使用的是自签发的证书,注意将根加入证书池。如果你使用的是可信CA签发的证书大部分不用添加,因为系统的可信CA库已经有了。如果没有成功添加, 在通信时会出现以下错误:
135 |
136 | > rpc error: code = Unavailable desc = all SubConns are in TransientFailure, latest connection error: connection error: desc = "transport: authentication handshake failed: x509: certificate signed by unknown authority"
137 |
138 | 或
139 |
140 | > rpc error: code = Unavailable desc = all SubConns are in TransientFailure, latest connection error: connection error: desc = "transport: authentication handshake failed: remote error: tls: bad certificate"
141 |
142 | - 客户端凭证内 `ServerName` 需要与服务器证书内的通用名称一致,如果不一致会出现如下错误:
143 |
144 | > rpc error: code = Unavailable desc = all SubConns are in TransientFailure, latest connection error: connection error: desc = "transport: authentication handshake failed: x509: certificate is valid for server.razeen.me, not xxxxx"
145 |
146 | 之后,我们就可安心的通信了,在私钥不泄漏的情况下,基本不再担心数据劫持问题了。
147 |
148 |
149 |
150 | **这里我想多说一句:**我们经常在提交代码时会直接 `git add .` ,这是个不好的习惯,有时后我们会将一些不必要的文件提交上去,特别是一些**证书**、**私钥**、**密码**之类的文件。
151 |
152 |
153 |
154 | ### 流式的RPC通信
155 |
156 | 流式PRC通信可以分为:
157 |
158 | - 服务器端流式 RPC;
159 |
160 | 客户端发送请求到服务器,拿到一个流去读取返回的消息序列。 客户端读取返回的流,直到里面没有任何消息。如:
161 |
162 | ```protobuf
163 | rpc ListHello(HelloWorldRequest) returns (stream HelloWorldResponse) {}
164 | ```
165 |
166 |
167 |
168 | - 客户端流式 RPC;
169 |
170 | 客户端写入一个消息序列并将其发送到服务器,同样也是使用流。一旦客户端完成写入消息,它等待服务器完成读取返回它的响应。如:
171 |
172 | ```protobuf
173 | rpc SayMoreHello(stream HelloWorldRequest) returns (HelloWorldResponse) {}
174 | ```
175 |
176 |
177 |
178 | - 双向流式 RPC;
179 |
180 | 双方使用读写流去发送一个消息序列。两个流独立操作,因此客户端和服务器可以以任意喜欢的顺序读写。如:
181 |
182 | ```protobuf
183 | rpc SayHelloChat(stream HelloWorldRequest) returns (stream HelloWorldRequest) {}
184 | ```
185 |
186 |
187 |
188 | 从上面的定义不难看出,用`stream`可以定义一个流式消息。下面我们就通过实例来演示一下流式通信的使用方法。
189 |
190 | 首先,我们将上面三个rpc server加入`.proto` , 并且生成新的`.pb.go`代码。
191 |
192 | 在生成的代码`hello_world.pb.go`中,我们可以看到客户端接口如下:
193 |
194 | ```go
195 | type HelloWorldServiceClient interface {
196 | SayHelloWorld(ctx context.Context, in *HelloWorldRequest, opts ...grpc.CallOption) (*HelloWorldResponse, error)
197 | ListHello(ctx context.Context, in *HelloWorldRequest, opts ...grpc.CallOption) (HelloWorldService_ListHelloClient, error)
198 | SayMoreHello(ctx context.Context, opts ...grpc.CallOption) (HelloWorldService_SayMoreHelloClient, error)
199 | SayHelloChat(ctx context.Context, opts ...grpc.CallOption) (HelloWorldService_SayHelloChatClient, error)
200 | }
201 | ```
202 |
203 | 服务端接口如下:
204 |
205 | ```go
206 | // HelloWorldServiceServer is the server API for HelloWorldService service.
207 | type HelloWorldServiceServer interface {
208 | SayHelloWorld(context.Context, *HelloWorldRequest) (*HelloWorldResponse, error)
209 | ListHello(*HelloWorldRequest, HelloWorldService_ListHelloServer) error
210 | SayMoreHello(HelloWorldService_SayMoreHelloServer) error
211 | SayHelloChat(HelloWorldService_SayHelloChatServer) error
212 | }
213 | ```
214 |
215 | 在客户段的接口中,生成了`HelloWorldService_XXXXClient`接口类型。 在服务端的接口中,生成了`HelloWorldService_XXXXServer`接口类型。 我们再查看这些接口的定义,发现这这几个接口都是实现了以下几个方法中的数个:
216 |
217 | ```go
218 | Send(*HelloWorldRequest) error
219 | Recv() (*HelloWorldRequest, error)
220 | CloseAndRecv() (*HelloWorldResponse, error)
221 | grpc.ClientStream
222 | ```
223 |
224 | 看其名字,我们不难知道,流式RPC的使用,或者说流的收发也就离不开这几个方法了。下面我们通过几个实例来验证一下。
225 |
226 |
227 |
228 | 在服务端,我们实现这三个接口。
229 |
230 | ```go
231 | // 服务器端流式 RPC, 接收一次客户端请求,返回一个流
232 | func (s *SayHelloServer) ListHello(in *pb.HelloWorldRequest, stream pb.HelloWorldService_ListHelloServer) error {
233 | log.Printf("Client Say: %v", in.Greeting)
234 |
235 | // 我们返回多条数据
236 | stream.Send(&pb.HelloWorldResponse{Reply: "ListHello Reply " + in.Greeting + " 1"})
237 | time.Sleep(1 * time.Second)
238 | stream.Send(&pb.HelloWorldResponse{Reply: "ListHello Reply " + in.Greeting + " 2"})
239 | time.Sleep(1 * time.Second)
240 | stream.Send(&pb.HelloWorldResponse{Reply: "ListHello Reply " + in.Greeting + " 3"})
241 | time.Sleep(1 * time.Second)
242 | return nil
243 | }
244 |
245 | // 客户端流式 RPC, 客户端流式请求,服务器可返回一次
246 | func (s *SayHelloServer) SayMoreHello(stream pb.HelloWorldService_SayMoreHelloServer) error {
247 | // 接受客户端请求
248 | for {
249 | req, err := stream.Recv()
250 | if err == io.EOF {
251 | break
252 | }
253 |
254 | if err != nil {
255 | return err
256 | }
257 |
258 | log.Printf("SayMoreHello Client Say: %v", req.Greeting)
259 | }
260 |
261 | // 流读取完成后,返回
262 | return stream.SendAndClose(&pb.HelloWorldResponse{Reply: "SayMoreHello Recv Muti Greeting"})
263 | }
264 |
265 | // 双向流式 RPC
266 | func (s *SayHelloServer) SayHelloChat(stream pb.HelloWorldService_SayHelloChatServer) error {
267 | // 开一个协程去处理客户端数据
268 | go func() {
269 | for {
270 | req, err := stream.Recv()
271 | if err == io.EOF {
272 | break
273 | }
274 |
275 | if err != nil {
276 | return
277 | }
278 |
279 | log.Printf("SayHelloChat Client Say: %v", req.Greeting)
280 | }
281 | }()
282 |
283 | // 向客户端写入多条数据
284 | stream.Send(&pb.HelloWorldRequest{Greeting: "SayHelloChat Server Say Hello 1"})
285 | time.Sleep(1 * time.Second)
286 | stream.Send(&pb.HelloWorldRequest{Greeting: "SayHelloChat Server Say Hello 2"})
287 | time.Sleep(1 * time.Second)
288 | stream.Send(&pb.HelloWorldRequest{Greeting: "SayHelloChat Server Say Hello 3"})
289 | time.Sleep(1 * time.Second)
290 | return nil
291 | }
292 | ```
293 |
294 |
295 |
296 | 之后我们就可以在客户端分别请求这几个rpc服务。
297 |
298 | ```go
299 | // 服务器端流式 RPC;
300 | // 我们向服务器SayHello
301 | recvListHello, err := client.ListHello(context.Background(), &pb.HelloWorldRequest{Greeting: "Hello Server List Hello"})
302 | if err != nil {
303 | log.Fatalf("ListHello err: %v", err)
304 | }
305 |
306 | // 服务器以流式返回
307 | // 直到 err==io.EOF时,表示接收完毕。
308 | for {
309 | resp, err := recvListHello.Recv()
310 | if err == io.EOF {
311 | break
312 | }
313 | if err != nil {
314 | log.Fatal(err)
315 | }
316 |
317 | log.Printf("ListHello Server Resp: %v", resp.Reply)
318 | }
319 | // Client Out:
320 | // 2018/08/06 01:27:55 ListHello Server Resp: ListHello Reply Hello Server List Hello 1
321 | // 2018/08/06 01:27:56 ListHello Server Resp: ListHello Reply Hello Server List Hello 2
322 | // 2018/08/06 01:27:57 ListHello Server Resp: ListHello Reply Hello Server List Hello 3
323 | // Server Out:
324 | // 2018/08/06 01:27:55 Client Say: Hello Server List Hello
325 |
326 |
327 | // 客户端流式 RPC;
328 | sayMoreClient, err := client.SayMoreHello(context.Background())
329 | if err != nil {
330 | log.Fatal(err)
331 | }
332 | for i := 0; i < 3; i++ {
333 | sayMoreClient.Send(&pb.HelloWorldRequest{Greeting: fmt.Sprintf("SayMoreHello Hello Server %d", i)})
334 | }
335 |
336 | sayMoreResp, err := sayMoreClient.CloseAndRecv()
337 | if err != nil {
338 | log.Fatal(err)
339 | }
340 | log.Printf("SayMoreHello Server Resp: %v", sayMoreResp.Reply)
341 |
342 | // Client Out:
343 | // 2018/08/06 01:31:11 SayMoreHello Server Resp: SayMoreHello Recv Muti Greeting
344 | // Server Out:
345 | // 2018/08/06 01:31:11 SayMoreHello Client Say: SayMoreHello Hello Server 0
346 | // 2018/08/06 01:31:11 SayMoreHello Client Say: SayMoreHello Hello Server 1
347 | // 2018/08/06 01:31:11 SayMoreHello Client Say: SayMoreHello Hello Server 2
348 |
349 |
350 | // 双向流式 RPC;
351 | sayHelloChat, err := client.SayHelloChat(context.Background())
352 | if err != nil {
353 | log.Fatal(err)
354 | }
355 |
356 | go func() {
357 | for i := 0; i < 3; i++ {
358 | sayHelloChat.Send(&pb.HelloWorldRequest{Greeting: fmt.Sprintf("SayHelloChat Hello Server %d", i)})
359 | }
360 | }()
361 |
362 | for {
363 | resp, err := sayHelloChat.Recv()
364 | if err == io.EOF {
365 | break
366 | }
367 | if err != nil {
368 | log.Fatal(err)
369 | }
370 |
371 | log.Printf("SayHelloChat Server Say: %v", resp.Greeting)
372 | }
373 | // Client Out:
374 | // 2018/08/06 01:31:11 SayHelloChat Server Say: SayHelloChat Server Say Hello 1
375 | // 2018/08/06 01:31:12 SayHelloChat Server Say: SayHelloChat Server Say Hello 2
376 | // 2018/08/06 01:31:13 SayHelloChat Server Say: SayHelloChat Server Say Hello 3
377 | // Server Out:
378 | // 2018/08/06 01:31:11 SayHelloChat Client Say: SayHelloChat Hello Server 0
379 | // 2018/08/06 01:31:11 SayHelloChat Client Say: SayHelloChat Hello Server 1
380 | // 2018/08/06 01:31:11 SayHelloChat Client Say: SayHelloChat Hello Server 2
381 | ```
382 |
383 | 看了实例,是不是觉得很简单~。三种方式大同小异,只要掌握了怎么去收发流,怎么判断流的结束,基本就可以了。
384 |
385 |
386 |
387 |
388 |
389 | 好了,gRPC在Go中的使用三篇文章到这里也就结束了,如果博客中有错误或者你还有想知道的,记得留言哦。
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
--------------------------------------------------------------------------------
/grpc/demo2/helloworld/hello_world.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go. DO NOT EDIT.
2 | // source: github.com/razeencheng/demo-go/grpc/demo2/helloworld/hello_world.proto
3 |
4 | package helloworld // import "github.com/razeencheng/demo-go/grpc/demo2/helloworld"
5 |
6 | import proto "github.com/golang/protobuf/proto"
7 | import fmt "fmt"
8 | import math "math"
9 | import any "github.com/golang/protobuf/ptypes/any"
10 |
11 | import (
12 | context "golang.org/x/net/context"
13 | grpc "google.golang.org/grpc"
14 | )
15 |
16 | // Reference imports to suppress errors if they are not otherwise used.
17 | var _ = proto.Marshal
18 | var _ = fmt.Errorf
19 | var _ = math.Inf
20 |
21 | // This is a compile-time assertion to ensure that this generated file
22 | // is compatible with the proto package it is being compiled against.
23 | // A compilation error at this line likely means your copy of the
24 | // proto package needs to be updated.
25 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
26 |
27 | type HelloWorldRequest struct {
28 | Greeting string `protobuf:"bytes,1,opt,name=greeting,proto3" json:"greeting,omitempty"`
29 | Infos map[string]string `protobuf:"bytes,2,rep,name=infos,proto3" json:"infos,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
30 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
31 | XXX_unrecognized []byte `json:"-"`
32 | XXX_sizecache int32 `json:"-"`
33 | }
34 |
35 | func (m *HelloWorldRequest) Reset() { *m = HelloWorldRequest{} }
36 | func (m *HelloWorldRequest) String() string { return proto.CompactTextString(m) }
37 | func (*HelloWorldRequest) ProtoMessage() {}
38 | func (*HelloWorldRequest) Descriptor() ([]byte, []int) {
39 | return fileDescriptor_hello_world_e1e511201326df80, []int{0}
40 | }
41 | func (m *HelloWorldRequest) XXX_Unmarshal(b []byte) error {
42 | return xxx_messageInfo_HelloWorldRequest.Unmarshal(m, b)
43 | }
44 | func (m *HelloWorldRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
45 | return xxx_messageInfo_HelloWorldRequest.Marshal(b, m, deterministic)
46 | }
47 | func (dst *HelloWorldRequest) XXX_Merge(src proto.Message) {
48 | xxx_messageInfo_HelloWorldRequest.Merge(dst, src)
49 | }
50 | func (m *HelloWorldRequest) XXX_Size() int {
51 | return xxx_messageInfo_HelloWorldRequest.Size(m)
52 | }
53 | func (m *HelloWorldRequest) XXX_DiscardUnknown() {
54 | xxx_messageInfo_HelloWorldRequest.DiscardUnknown(m)
55 | }
56 |
57 | var xxx_messageInfo_HelloWorldRequest proto.InternalMessageInfo
58 |
59 | func (m *HelloWorldRequest) GetGreeting() string {
60 | if m != nil {
61 | return m.Greeting
62 | }
63 | return ""
64 | }
65 |
66 | func (m *HelloWorldRequest) GetInfos() map[string]string {
67 | if m != nil {
68 | return m.Infos
69 | }
70 | return nil
71 | }
72 |
73 | type HelloWorldResponse struct {
74 | Reply string `protobuf:"bytes,1,opt,name=reply,proto3" json:"reply,omitempty"`
75 | Details []*any.Any `protobuf:"bytes,2,rep,name=details,proto3" json:"details,omitempty"`
76 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
77 | XXX_unrecognized []byte `json:"-"`
78 | XXX_sizecache int32 `json:"-"`
79 | }
80 |
81 | func (m *HelloWorldResponse) Reset() { *m = HelloWorldResponse{} }
82 | func (m *HelloWorldResponse) String() string { return proto.CompactTextString(m) }
83 | func (*HelloWorldResponse) ProtoMessage() {}
84 | func (*HelloWorldResponse) Descriptor() ([]byte, []int) {
85 | return fileDescriptor_hello_world_e1e511201326df80, []int{1}
86 | }
87 | func (m *HelloWorldResponse) XXX_Unmarshal(b []byte) error {
88 | return xxx_messageInfo_HelloWorldResponse.Unmarshal(m, b)
89 | }
90 | func (m *HelloWorldResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
91 | return xxx_messageInfo_HelloWorldResponse.Marshal(b, m, deterministic)
92 | }
93 | func (dst *HelloWorldResponse) XXX_Merge(src proto.Message) {
94 | xxx_messageInfo_HelloWorldResponse.Merge(dst, src)
95 | }
96 | func (m *HelloWorldResponse) XXX_Size() int {
97 | return xxx_messageInfo_HelloWorldResponse.Size(m)
98 | }
99 | func (m *HelloWorldResponse) XXX_DiscardUnknown() {
100 | xxx_messageInfo_HelloWorldResponse.DiscardUnknown(m)
101 | }
102 |
103 | var xxx_messageInfo_HelloWorldResponse proto.InternalMessageInfo
104 |
105 | func (m *HelloWorldResponse) GetReply() string {
106 | if m != nil {
107 | return m.Reply
108 | }
109 | return ""
110 | }
111 |
112 | func (m *HelloWorldResponse) GetDetails() []*any.Any {
113 | if m != nil {
114 | return m.Details
115 | }
116 | return nil
117 | }
118 |
119 | type HelloWorld struct {
120 | Msg string `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"`
121 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
122 | XXX_unrecognized []byte `json:"-"`
123 | XXX_sizecache int32 `json:"-"`
124 | }
125 |
126 | func (m *HelloWorld) Reset() { *m = HelloWorld{} }
127 | func (m *HelloWorld) String() string { return proto.CompactTextString(m) }
128 | func (*HelloWorld) ProtoMessage() {}
129 | func (*HelloWorld) Descriptor() ([]byte, []int) {
130 | return fileDescriptor_hello_world_e1e511201326df80, []int{2}
131 | }
132 | func (m *HelloWorld) XXX_Unmarshal(b []byte) error {
133 | return xxx_messageInfo_HelloWorld.Unmarshal(m, b)
134 | }
135 | func (m *HelloWorld) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
136 | return xxx_messageInfo_HelloWorld.Marshal(b, m, deterministic)
137 | }
138 | func (dst *HelloWorld) XXX_Merge(src proto.Message) {
139 | xxx_messageInfo_HelloWorld.Merge(dst, src)
140 | }
141 | func (m *HelloWorld) XXX_Size() int {
142 | return xxx_messageInfo_HelloWorld.Size(m)
143 | }
144 | func (m *HelloWorld) XXX_DiscardUnknown() {
145 | xxx_messageInfo_HelloWorld.DiscardUnknown(m)
146 | }
147 |
148 | var xxx_messageInfo_HelloWorld proto.InternalMessageInfo
149 |
150 | func (m *HelloWorld) GetMsg() string {
151 | if m != nil {
152 | return m.Msg
153 | }
154 | return ""
155 | }
156 |
157 | type Error struct {
158 | Msg []string `protobuf:"bytes,1,rep,name=msg,proto3" json:"msg,omitempty"`
159 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
160 | XXX_unrecognized []byte `json:"-"`
161 | XXX_sizecache int32 `json:"-"`
162 | }
163 |
164 | func (m *Error) Reset() { *m = Error{} }
165 | func (m *Error) String() string { return proto.CompactTextString(m) }
166 | func (*Error) ProtoMessage() {}
167 | func (*Error) Descriptor() ([]byte, []int) {
168 | return fileDescriptor_hello_world_e1e511201326df80, []int{3}
169 | }
170 | func (m *Error) XXX_Unmarshal(b []byte) error {
171 | return xxx_messageInfo_Error.Unmarshal(m, b)
172 | }
173 | func (m *Error) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
174 | return xxx_messageInfo_Error.Marshal(b, m, deterministic)
175 | }
176 | func (dst *Error) XXX_Merge(src proto.Message) {
177 | xxx_messageInfo_Error.Merge(dst, src)
178 | }
179 | func (m *Error) XXX_Size() int {
180 | return xxx_messageInfo_Error.Size(m)
181 | }
182 | func (m *Error) XXX_DiscardUnknown() {
183 | xxx_messageInfo_Error.DiscardUnknown(m)
184 | }
185 |
186 | var xxx_messageInfo_Error proto.InternalMessageInfo
187 |
188 | func (m *Error) GetMsg() []string {
189 | if m != nil {
190 | return m.Msg
191 | }
192 | return nil
193 | }
194 |
195 | func init() {
196 | proto.RegisterType((*HelloWorldRequest)(nil), "helloworld.HelloWorldRequest")
197 | proto.RegisterMapType((map[string]string)(nil), "helloworld.HelloWorldRequest.InfosEntry")
198 | proto.RegisterType((*HelloWorldResponse)(nil), "helloworld.HelloWorldResponse")
199 | proto.RegisterType((*HelloWorld)(nil), "helloworld.HelloWorld")
200 | proto.RegisterType((*Error)(nil), "helloworld.Error")
201 | }
202 |
203 | // Reference imports to suppress errors if they are not otherwise used.
204 | var _ context.Context
205 | var _ grpc.ClientConn
206 |
207 | // This is a compile-time assertion to ensure that this generated file
208 | // is compatible with the grpc package it is being compiled against.
209 | const _ = grpc.SupportPackageIsVersion4
210 |
211 | // HelloWorldServiceClient is the client API for HelloWorldService service.
212 | //
213 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
214 | type HelloWorldServiceClient interface {
215 | SayHelloWorld(ctx context.Context, in *HelloWorldRequest, opts ...grpc.CallOption) (*HelloWorldResponse, error)
216 | }
217 |
218 | type helloWorldServiceClient struct {
219 | cc *grpc.ClientConn
220 | }
221 |
222 | func NewHelloWorldServiceClient(cc *grpc.ClientConn) HelloWorldServiceClient {
223 | return &helloWorldServiceClient{cc}
224 | }
225 |
226 | func (c *helloWorldServiceClient) SayHelloWorld(ctx context.Context, in *HelloWorldRequest, opts ...grpc.CallOption) (*HelloWorldResponse, error) {
227 | out := new(HelloWorldResponse)
228 | err := c.cc.Invoke(ctx, "/helloworld.HelloWorldService/SayHelloWorld", in, out, opts...)
229 | if err != nil {
230 | return nil, err
231 | }
232 | return out, nil
233 | }
234 |
235 | // HelloWorldServiceServer is the server API for HelloWorldService service.
236 | type HelloWorldServiceServer interface {
237 | SayHelloWorld(context.Context, *HelloWorldRequest) (*HelloWorldResponse, error)
238 | }
239 |
240 | func RegisterHelloWorldServiceServer(s *grpc.Server, srv HelloWorldServiceServer) {
241 | s.RegisterService(&_HelloWorldService_serviceDesc, srv)
242 | }
243 |
244 | func _HelloWorldService_SayHelloWorld_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
245 | in := new(HelloWorldRequest)
246 | if err := dec(in); err != nil {
247 | return nil, err
248 | }
249 | if interceptor == nil {
250 | return srv.(HelloWorldServiceServer).SayHelloWorld(ctx, in)
251 | }
252 | info := &grpc.UnaryServerInfo{
253 | Server: srv,
254 | FullMethod: "/helloworld.HelloWorldService/SayHelloWorld",
255 | }
256 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
257 | return srv.(HelloWorldServiceServer).SayHelloWorld(ctx, req.(*HelloWorldRequest))
258 | }
259 | return interceptor(ctx, in, info, handler)
260 | }
261 |
262 | var _HelloWorldService_serviceDesc = grpc.ServiceDesc{
263 | ServiceName: "helloworld.HelloWorldService",
264 | HandlerType: (*HelloWorldServiceServer)(nil),
265 | Methods: []grpc.MethodDesc{
266 | {
267 | MethodName: "SayHelloWorld",
268 | Handler: _HelloWorldService_SayHelloWorld_Handler,
269 | },
270 | },
271 | Streams: []grpc.StreamDesc{},
272 | Metadata: "github.com/razeencheng/demo-go/grpc/demo2/helloworld/hello_world.proto",
273 | }
274 |
275 | func init() {
276 | proto.RegisterFile("github.com/razeencheng/demo-go/grpc/demo2/helloworld/hello_world.proto", fileDescriptor_hello_world_e1e511201326df80)
277 | }
278 |
279 | var fileDescriptor_hello_world_e1e511201326df80 = []byte{
280 | // 336 bytes of a gzipped FileDescriptorProto
281 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x52, 0x4b, 0x4b, 0xc3, 0x40,
282 | 0x10, 0x36, 0x2d, 0xf5, 0x31, 0x22, 0xe8, 0xd2, 0x43, 0x0d, 0x58, 0x4a, 0x4e, 0xbd, 0xb8, 0x0b,
283 | 0x55, 0xa4, 0x78, 0x10, 0x14, 0x2a, 0x7a, 0x93, 0xf4, 0x20, 0xf4, 0x22, 0x69, 0x3a, 0xdd, 0x06,
284 | 0xb7, 0x3b, 0x71, 0x93, 0x54, 0xe2, 0x3f, 0xf2, 0x5f, 0xca, 0x26, 0x8d, 0x09, 0x88, 0x1e, 0x3c,
285 | 0x04, 0xe6, 0xdb, 0xef, 0x31, 0x0f, 0x02, 0xf7, 0x32, 0x4a, 0x57, 0xd9, 0x9c, 0x87, 0xb4, 0x16,
286 | 0x26, 0xf8, 0x40, 0xd4, 0xe1, 0x0a, 0xb5, 0x14, 0x0b, 0x5c, 0xd3, 0xb9, 0x24, 0x21, 0x4d, 0x1c,
287 | 0x16, 0x60, 0x24, 0x56, 0xa8, 0x14, 0xbd, 0x93, 0x51, 0x8b, 0xb2, 0x7c, 0x29, 0x6a, 0x1e, 0x1b,
288 | 0x4a, 0x89, 0x41, 0xcd, 0xba, 0xa2, 0x91, 0x29, 0x49, 0x05, 0x5a, 0x8a, 0x42, 0x34, 0xcf, 0x96,
289 | 0x22, 0x4e, 0xf3, 0x18, 0x13, 0x11, 0xe8, 0xdc, 0x7e, 0xa5, 0xd9, 0xfb, 0x74, 0xe0, 0xe4, 0xc1,
290 | 0xfa, 0x9f, 0xad, 0xdf, 0xc7, 0xb7, 0x0c, 0x93, 0x94, 0xb9, 0xb0, 0x2f, 0x0d, 0x62, 0x1a, 0x69,
291 | 0xd9, 0x73, 0x06, 0xce, 0xf0, 0xc0, 0xff, 0xc6, 0xec, 0x06, 0x3a, 0x91, 0x5e, 0x52, 0xd2, 0x6b,
292 | 0x0d, 0xda, 0xc3, 0xc3, 0xd1, 0x90, 0xd7, 0xed, 0xf9, 0x8f, 0x24, 0xfe, 0x68, 0xa5, 0x13, 0x9d,
293 | 0x9a, 0xdc, 0x2f, 0x6d, 0xee, 0x18, 0xa0, 0x7e, 0x64, 0xc7, 0xd0, 0x7e, 0xc5, 0x7c, 0xdb, 0xc4,
294 | 0x96, 0xac, 0x0b, 0x9d, 0x4d, 0xa0, 0x32, 0xec, 0xb5, 0x8a, 0xb7, 0x12, 0x5c, 0xb7, 0xc6, 0x8e,
295 | 0x37, 0x03, 0xd6, 0x6c, 0x90, 0xc4, 0xa4, 0x13, 0xb4, 0x7a, 0x83, 0xb1, 0xaa, 0x32, 0x4a, 0xc0,
296 | 0x38, 0xec, 0x2d, 0x30, 0x0d, 0x22, 0x55, 0xcd, 0xd9, 0xe5, 0x92, 0x48, 0x2a, 0xe4, 0xd5, 0x3d,
297 | 0xf8, 0xad, 0xce, 0xfd, 0x4a, 0xe4, 0xf5, 0x01, 0xea, 0x6c, 0x3b, 0xd5, 0x3a, 0xa9, 0x56, 0xb7,
298 | 0xa5, 0x77, 0x0a, 0x9d, 0x89, 0x31, 0x64, 0x6a, 0xaa, 0xbd, 0xa5, 0x46, 0xd8, 0xbc, 0xe0, 0x14,
299 | 0xcd, 0x26, 0x0a, 0x91, 0x3d, 0xc1, 0xd1, 0x34, 0xc8, 0x1b, 0x91, 0x67, 0x7f, 0xde, 0xc9, 0xed,
300 | 0xff, 0x46, 0x97, 0x5b, 0x7a, 0x3b, 0x77, 0x57, 0xb3, 0xcb, 0xff, 0xfc, 0x30, 0xf3, 0xdd, 0x62,
301 | 0xe1, 0x8b, 0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcf, 0x1c, 0x94, 0xd7, 0x6f, 0x02, 0x00, 0x00,
302 | }
303 |
--------------------------------------------------------------------------------