├── _config.yml ├── .gitignore ├── .travis.yml ├── .github └── workflows │ └── main.yml ├── compress.go ├── go.mod ├── doc.go ├── utility.go ├── utility_test.go ├── docs ├── httprpc.md ├── quickstart_server.md ├── quickstart_client.md └── Demo.md ├── header_test.go ├── request.pb_test.go ├── log.go ├── example ├── haclient_example_test.go ├── client_example_test.go ├── pb_example_test.go └── server_example_test.go ├── codec_test.go ├── README.md ├── benchmark_test.go ├── header.go ├── status_test.go ├── haclient_test.go ├── echoservice_test.go ├── go.sum ├── httpserver_test.go ├── datamessage_test.go ├── server_test.go ├── status.go ├── connection.go ├── connectionpool.go ├── rpcpackage_test.go ├── httpserver.go ├── haclient.go ├── codec.go ├── pb_status.go ├── LICENSE ├── client.go ├── client_test.go ├── rpcpackage.go └── pb_data.go /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-architect -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .settings/org.eclipse.core.resources.prefs 3 | .idea 4 | .vscode/launch.json 5 | .gitignore 6 | .vscode/settings.json 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go_import_path: github.com/jhunters/timewheel 3 | 4 | sudo: false 5 | 6 | go: 7 | - 1.13 8 | 9 | before_install: 10 | - go get -v honnef.co/go/tools/... 11 | - go get -v github.com/kisielk/errcheck 12 | 13 | script: 14 | - make test 15 | - make race 16 | # - make errcheck 17 | 18 | after_success: 19 | - bash <(curl -s https://codecov.io/bash) -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ v1.4.x ] 6 | pull_request: 7 | branches: [ v1.4.x ] 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v2 18 | with: 19 | go-version: 1.18 20 | 21 | - name: Build 22 | run: go build -v ./... 23 | 24 | - name: Test 25 | run: go test -timeout 120s -v ./... -coverprofile=coverage.txt -covermode=atomic 26 | 27 | - name: CoverageUpload 28 | run: bash <(curl -s https://codecov.io/bash) 29 | -------------------------------------------------------------------------------- /compress.go: -------------------------------------------------------------------------------- 1 | package baidurpc 2 | 3 | import ( 4 | "bytes" 5 | "compress/gzip" 6 | "io/ioutil" 7 | ) 8 | 9 | // GZIP do gzip action by gzip package 10 | func GZIP(b []byte) ([]byte, error) { 11 | buf := new(bytes.Buffer) 12 | w := gzip.NewWriter(buf) 13 | defer w.Close() 14 | 15 | _, err := w.Write(b) 16 | w.Flush() 17 | 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | return buf.Bytes(), nil 23 | } 24 | 25 | // GUNZIP do unzip action by gzip package 26 | func GUNZIP(b []byte) ([]byte, error) { 27 | buf := new(bytes.Buffer) 28 | buf.Write(b) 29 | r, err := gzip.NewReader(buf) 30 | if err != nil { 31 | return nil, err 32 | } 33 | defer r.Close() 34 | undatas, _ := ioutil.ReadAll(r) 35 | 36 | return undatas, nil 37 | } 38 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/baidu-golang/pbrpc 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/golang/snappy v0.0.4 7 | github.com/jhunters/goassist v1.0.9 8 | github.com/jhunters/timewheel v1.1.0 9 | github.com/jolestar/go-commons-pool/v2 v2.1.1 10 | github.com/smartystreets/goconvey v1.7.2 11 | google.golang.org/protobuf v1.26.0 12 | ) 13 | 14 | require ( 15 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect 16 | github.com/jtolds/gls v4.20.0+incompatible // indirect 17 | github.com/smartystreets/assertions v1.2.0 // indirect 18 | ) 19 | 20 | require ( 21 | github.com/jhunters/link v0.0.0-20221207123848-6322292415b2 22 | github.com/stretchr/testify v1.6.1 // indirect 23 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect 24 | ) 25 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 2 | // 3 | // Copyright 2002-2007 the original author or authors. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | package baidurpc 17 | -------------------------------------------------------------------------------- /utility.go: -------------------------------------------------------------------------------- 1 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 2 | // 3 | // Copyright 2002-2007 the original author or authors. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | package baidurpc 17 | 18 | import ( 19 | "time" 20 | ) 21 | 22 | var NANO_IN_SECONDS = 1000000000.0 23 | 24 | // get time took in seconds 25 | func TimetookInSeconds(currentNano int64) float64 { 26 | c := time.Now().UnixNano() 27 | 28 | return float64(c-currentNano) / NANO_IN_SECONDS 29 | } 30 | -------------------------------------------------------------------------------- /utility_test.go: -------------------------------------------------------------------------------- 1 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 2 | // 3 | // Copyright 2002-2007 the original author or authors. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | package baidurpc_test 17 | 18 | import ( 19 | "testing" 20 | "time" 21 | 22 | baidurpc "github.com/baidu-golang/pbrpc" 23 | ) 24 | 25 | func TestTimetookInSeconds(t *testing.T) { 26 | 27 | now := time.Now().UnixNano() 28 | 29 | time.Sleep(time.Second) 30 | 31 | cost := baidurpc.TimetookInSeconds(now) 32 | 33 | if cost < 1 { 34 | t.Error("time took is not acceptable.") 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /docs/httprpc.md: -------------------------------------------------------------------------------- 1 | 6 |

baidurpc

7 | 8 |

9 | baidurpc是一种基于TCP协议的二进制高性能RPC通信协议实现。它以Protobuf作为基本的数据交换格式。 10 | 本版本基于golang实现.完全兼容jprotobuf-rpc-socket: https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 11 |

12 | 13 | 14 | 15 | ### 同步发布http rpc服务 16 | 要同步发布http rpc 服务,部署非常简单,直接调用 EnableHttp 方法即可。会与rpc复用同一端口。 17 | 18 | ```go 19 | rpcServer.EnableHttp() 20 | ``` 21 | 22 | ```go 23 | serverMeta := baidurpc.ServerMeta{} 24 | serverMeta.Host = nil 25 | serverMeta.Port = Int(*port) 26 | rpcServer := baidurpc.NewTpcServer(&serverMeta) 27 | 28 | echoService := new(EchoService) 29 | 30 | rpcServer.Register(echoService) 31 | // 开启http rpc服务 32 | rpcServer.EnableHttp() 33 | // 启动RPC服务 34 | err := rpcServer.Start() 35 | 36 | if err != nil { 37 | baidurpc.Error(err) 38 | } 39 | ``` 40 | 41 | ### 相关事项说明 42 | 1. 发布http rpc 服务, 需要struct 增加json tag字段说明 43 | ```go 44 | type DataMessage struct { 45 | Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` 46 | } 47 | ``` 48 | 2. http 协议统一使用POST方法进行发布。 url path 协同 http://host:port/rpc/service_name/method_name 49 | 3. http请求内容 50 | header 定义 "Content-Type" "application/json;charset=utf-8" 51 | POST body直接为json结构 52 | 4. http返回内容, json结果. 53 |
54 |    errno错误码: 0表示成功,其它则为错误
55 |    message: 错误信息,errno非0时,会设置
56 |    data:返回内容
57 |    
58 | ```json 59 | { 60 | "errno" : 0, 61 | "message" : "", 62 | "data" : {} 63 | } 64 | ``` -------------------------------------------------------------------------------- /header_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: Malin Xie 3 | * @Description: 4 | * @Date: 2021-04-26 18:18:59 5 | */ 6 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 7 | // 8 | // Copyright 2002-2007 the original author or authors. 9 | // 10 | // Licensed under the Apache License, Version 2.0 (the "License"); 11 | // you may not use this file except in compliance with the License. 12 | // You may obtain a copy of the License at 13 | // 14 | // http://www.apache.org/licenses/LICENSE-2.0 15 | // 16 | // Unless required by applicable law or agreed to in writing, software 17 | // distributed under the License is distributed on an "AS IS" BASIS, 18 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | // See the License for the specific language governing permissions and 20 | // limitations under the License. 21 | package baidurpc_test 22 | 23 | import ( 24 | "testing" 25 | 26 | baidurpc "github.com/baidu-golang/pbrpc" 27 | 28 | . "github.com/smartystreets/goconvey/convey" 29 | ) 30 | 31 | // TestRpcDataWriteReader test head read and write 32 | func TestRpcDataWriteReader(t *testing.T) { 33 | 34 | Convey("TestRpcDataWriteReader", t, func() { 35 | 36 | h := baidurpc.Header{} 37 | h.SetMagicCode([]byte("PRPB")) 38 | h.SetMessageSize(12300) 39 | h.SetMetaSize(59487) 40 | 41 | bs, _ := h.Write() 42 | So(len(bs), ShouldEqual, baidurpc.SIZE) 43 | 44 | h2 := baidurpc.Header{} 45 | h2.Read(bs) 46 | So(string(h.GetMagicCode()), ShouldEqual, string(h2.GetMagicCode())) 47 | So(h.GetMessageSize(), ShouldEqual, 12300) 48 | So(h.GetMetaSize(), ShouldEqual, 59487) 49 | }) 50 | 51 | } 52 | -------------------------------------------------------------------------------- /docs/quickstart_server.md: -------------------------------------------------------------------------------- 1 | 6 |

baidurpc

7 | 8 |

9 | baidurpc是一种基于TCP协议的二进制高性能RPC通信协议实现。它以Protobuf作为基本的数据交换格式。 10 | 本版本基于golang实现.完全兼容jprotobuf-rpc-socket: https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 11 |

12 | 13 | 14 | ### Installing 15 | 16 | To start using pbrpc, install Go and run `go get`: 17 | 18 | ```sh 19 | $ go get github.com/baidu-golang/pbrpc 20 | ``` 21 | 22 | ### 定义protobuf 对象 23 | ```property 24 | message DataMessae { 25 | string name = 1; 26 | } 27 | ``` 28 | 用protoc工具 生成 pb go 定义文件 29 | protoc --go_out=. datamessage.proto 30 | 31 | 32 | ### 开发RPC服务端 33 | 要发布一个RPC服务,需要先定义一个对象以及方法,用于发布服务。 与传统的golang基础库的rpc发布非常一致。 34 | 35 | 1. 以下定义了 EchoService对象 并 增加 Echo 方法 36 | ```go 37 | type EchoService struct { 38 | } 39 | 40 | func (rpc *EchoService) Echo(c context.Context, in *DataMessage) (*DataMessage, context.Context) { 41 | var ret = "hello " 42 | if len(*in.Name) == 0 { 43 | ret = ret + "veryone" 44 | } else { 45 | ret = ret + *in.Name 46 | } 47 | dm := DataMessage{} 48 | dm.Name = proto.String(ret) 49 | return &dm, baidurpc.BindAttachement(context.Background(), []byte("hello")) // return with attachement 50 | } 51 | ``` 52 | 53 | 2. 指定发布端口,把EchoService发布成RPC服务 54 | 55 | ```go 56 | serverMeta := baidurpc.ServerMeta{} 57 | serverMeta.Host = nil 58 | serverMeta.Port = Int(*port) 59 | rpcServer := baidurpc.NewTpcServer(&serverMeta) 60 | 61 | echoService := new(EchoService) 62 | 63 | rpcServer.Register(echoService) 64 | 65 | // 启动RPC服务 66 | err := rpcServer.Start() 67 | 68 | if err != nil { 69 | baidurpc.Error(err) 70 | } 71 | ``` 72 | 73 | [查看代码](https://github.com/baidu-golang/pbrpc/blob/master/example/server_example_test.go)
-------------------------------------------------------------------------------- /docs/quickstart_client.md: -------------------------------------------------------------------------------- 1 | 6 |

baidurpc

7 | 8 |

9 | baidurpc是一种基于TCP协议的二进制高性能RPC通信协议实现。它以Protobuf作为基本的数据交换格式。 10 | 本版本基于golang实现.完全兼容jprotobuf-rpc-socket: https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 11 |

12 | 13 | 14 | ### Installing 15 | 16 | To start using pbrpc, install Go and run `go get`: 17 | 18 | ```sh 19 | $ go get github.com/baidu-golang/pbrpc 20 | ``` 21 | 22 | ### 定义protobuf 对象 23 | ```property 24 | message DataMessae { 25 | string name = 1; 26 | } 27 | ``` 28 | 用protoc工具 生成 pb go 定义文件 29 | protoc --go_out=. datamessage.proto 30 | 31 | 32 | ### 开发RPC客户端 33 | 客户端开发,使用RpcClient进行调用。 34 | 35 | ```go 36 | host := "localhost" 37 | port := 1031 38 | // 创建链接 39 | url := baidurpc.URL{} 40 | url.SetHost(&host).SetPort(&port) 41 | 42 | timeout := time.Second * 500 43 | // create client by simple connection 44 | connection, err := baidurpc.NewTCPConnection(url, &timeout) 45 | 46 | if err != nil { 47 | fmt.Println(err) 48 | return 49 | } 50 | defer connection.Close() 51 | 52 | // 创建client 53 | rpcClient, err := baidurpc.NewRpcCient(connection) 54 | if err != nil { 55 | fmt.Println(err) 56 | return 57 | } 58 | defer rpcClient.Close() 59 | // 调用RPC 60 | serviceName := "echoService" 61 | methodName := "echo" 62 | rpcInvocation := baidurpc.NewRpcInvocation(&serviceName, &methodName) 63 | 64 | message := "say hello from xiemalin中文测试" 65 | dm := DataMessage{&message} 66 | 67 | rpcInvocation.SetParameterIn(&dm) 68 | parameterOut := DataMessage{} 69 | 70 | _, err := rpcClient.SendRpcRequest(rpcInvocation, ¶meterOut) 71 | if err != nil { 72 | fmt.Println(err) 73 | } 74 | 75 | fmt.Println(*parameterOut.Name) 76 | ``` 77 | 78 | [查看代码](https://github.com/baidu-golang/pbrpc/blob/master/example/client_example_test.go)
-------------------------------------------------------------------------------- /request.pb_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: Malin Xie 3 | * @Description: 4 | * @Date: 2021-07-24 16:54:14 5 | */ 6 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 7 | // 8 | // Copyright 2002-2007 the original author or authors. 9 | // 10 | // Licensed under the Apache License, Version 2.0 (the "License"); 11 | // you may not use this file except in compliance with the License. 12 | // You may obtain a copy of the License at 13 | // 14 | // http://www.apache.org/licenses/LICENSE-2.0 15 | // 16 | // Unless required by applicable law or agreed to in writing, software 17 | // distributed under the License is distributed on an "AS IS" BASIS, 18 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | // See the License for the specific language governing permissions and 20 | // limitations under the License. 21 | package baidurpc_test 22 | 23 | import ( 24 | "testing" 25 | 26 | baidurpc "github.com/baidu-golang/pbrpc" 27 | "google.golang.org/protobuf/proto" 28 | 29 | . "github.com/smartystreets/goconvey/convey" 30 | ) 31 | 32 | // TestPropertySetAndGet 33 | func TestPropertySetAndGet(t *testing.T) { 34 | Convey("TestPropertySetAndGet", t, func() { 35 | var serviceName string = "ThisAServiceName" 36 | var methodName string = "ThisAMethodName" 37 | var logId int64 = 1 38 | 39 | request := baidurpc.Request{ 40 | ServiceName: serviceName, 41 | MethodName: methodName, 42 | LogId: logId, 43 | } 44 | 45 | So(serviceName, ShouldEqual, request.GetServiceName()) 46 | So(methodName, ShouldEqual, request.GetMethodName()) 47 | So(logId, ShouldEqual, request.GetLogId()) 48 | 49 | data, err := proto.Marshal(&request) 50 | So(err, ShouldBeNil) 51 | 52 | request2 := new(baidurpc.Request) 53 | err = proto.Unmarshal(data, request2) 54 | So(err, ShouldBeNil) 55 | 56 | So(request.GetServiceName(), ShouldEqual, request2.GetServiceName()) 57 | }) 58 | 59 | } 60 | -------------------------------------------------------------------------------- /log.go: -------------------------------------------------------------------------------- 1 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 2 | // 3 | // Copyright 2002-2007 the original author or authors. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | package baidurpc 17 | 18 | import ( 19 | "log" 20 | ) 21 | 22 | // Info logs to the INFO log. 23 | // Arguments are handled in the manner of fmt.Print; a newline is appended if missing. 24 | func Info(args ...interface{}) { 25 | log.Println(args...) 26 | } 27 | 28 | // Infof logs to the INFO log. 29 | // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. 30 | func Infof(format string, args ...interface{}) { 31 | log.Printf(format, args...) 32 | } 33 | 34 | // Warning logs to the WARNING and INFO logs. 35 | // Arguments are handled in the manner of fmt.Print; a newline is appended if missing. 36 | func Warning(args ...interface{}) { 37 | log.Println(args...) 38 | } 39 | 40 | // Warningf logs to the WARNING and INFO logs. 41 | // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. 42 | func Warningf(format string, args ...interface{}) { 43 | log.Printf(format, args...) 44 | } 45 | 46 | // Error logs to the ERROR, WARNING, and INFO logs. 47 | // Arguments are handled in the manner of fmt.Print; a newline is appended if missing. 48 | func Error(args ...interface{}) { 49 | log.Println(args...) 50 | } 51 | 52 | // Errorf logs to the ERROR, WARNING, and INFO logs. 53 | // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. 54 | func Errorf(format string, args ...interface{}) { 55 | log.Printf(format, args...) 56 | } 57 | -------------------------------------------------------------------------------- /example/haclient_example_test.go: -------------------------------------------------------------------------------- 1 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 2 | // 3 | // Copyright 2002-2007 the original author or authors. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | /* 17 | * @Author: Malin Xie 18 | * @Description: 19 | * @Date: 2021-08-03 19:12:14 20 | */ 21 | package example 22 | 23 | import ( 24 | "fmt" 25 | "time" 26 | 27 | baidurpc "github.com/baidu-golang/pbrpc" 28 | "google.golang.org/protobuf/proto" 29 | ) 30 | 31 | // ExampleTcpClient 32 | func ExampleHaTcpClient() { 33 | 34 | host := "localhost" 35 | timeout := time.Second * 5 36 | errPort := 100 37 | port := 1031 38 | urls := []baidurpc.URL{{Host: &host, Port: &errPort}, {Host: &host, Port: &port}} 39 | 40 | connections, err := baidurpc.NewBatchTCPConnection(urls, timeout) 41 | if err != nil { 42 | return 43 | } 44 | defer baidurpc.CloseBatchConnection(connections) 45 | 46 | haClient, err := baidurpc.NewHaRpcCient(connections) 47 | if err != nil { 48 | return 49 | } 50 | defer haClient.Close() 51 | 52 | serviceName := "echoService" 53 | methodName := "echo" 54 | rpcInvocation := baidurpc.NewRpcInvocation(&serviceName, &methodName) 55 | 56 | message := "say hello from xiemalin中文测试" 57 | dm := DataMessage{Name: message} 58 | 59 | rpcInvocation.SetParameterIn(&dm) 60 | rpcInvocation.LogId = proto.Int64(1) 61 | rpcInvocation.Attachment = []byte("hello world") 62 | 63 | parameterOut := DataMessage{} 64 | 65 | response, err := haClient.SendRpcRequest(rpcInvocation, ¶meterOut) 66 | if err != nil { 67 | return 68 | } 69 | 70 | if response == nil { 71 | fmt.Println("Reponse is nil") 72 | return 73 | } 74 | 75 | fmt.Println("attachement", response.Attachment) 76 | 77 | } 78 | -------------------------------------------------------------------------------- /codec_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: Malin Xie 3 | * @Description: 4 | * @Date: 2021-07-24 16:54:14 5 | */ 6 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 7 | // 8 | // Copyright 2002-2007 the original author or authors. 9 | // 10 | // Licensed under the Apache License, Version 2.0 (the "License"); 11 | // you may not use this file except in compliance with the License. 12 | // You may obtain a copy of the License at 13 | // 14 | // http://www.apache.org/licenses/LICENSE-2.0 15 | // 16 | // Unless required by applicable law or agreed to in writing, software 17 | // distributed under the License is distributed on an "AS IS" BASIS, 18 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | // See the License for the specific language governing permissions and 20 | // limitations under the License. 21 | package baidurpc_test 22 | 23 | import ( 24 | "bytes" 25 | "testing" 26 | 27 | baidurpc "github.com/baidu-golang/pbrpc" 28 | . "github.com/smartystreets/goconvey/convey" 29 | ) 30 | 31 | // TestCodecSend 32 | func TestCodecSend(t *testing.T) { 33 | Convey("TestCodecSend", t, func() { 34 | protocol := &baidurpc.RpcDataPackageProtocol[*baidurpc.RpcDataPackage, *baidurpc.RpcDataPackage]{} 35 | 36 | buf := new(bytes.Buffer) 37 | 38 | rpcDataPackagecodec, _ := protocol.NewCodec(buf) 39 | 40 | rpcDataPackage := initRpcDataPackage() 41 | 42 | rpcDataPackagecodec.Send(rpcDataPackage) 43 | 44 | r2 := baidurpc.RpcDataPackage{} 45 | err := r2.ReadIO(buf) 46 | So(err, ShouldBeNil) 47 | 48 | err = equalRpcDataPackage(r2, t) 49 | So(err, ShouldBeNil) 50 | }) 51 | 52 | } 53 | 54 | // TestCodecReceive 55 | func TestCodecReceive(t *testing.T) { 56 | 57 | Convey("TestCodecReceive", t, func() { 58 | protocol := &baidurpc.RpcDataPackageProtocol[*baidurpc.RpcDataPackage, *baidurpc.RpcDataPackage]{} 59 | 60 | buf := new(bytes.Buffer) 61 | // write prepare value to buf 62 | rpcDataPackage := initRpcDataPackage() 63 | bytes, err := rpcDataPackage.Write() 64 | So(err, ShouldBeNil) 65 | 66 | buf.Write(bytes) 67 | rpcDataPackagecodec, _ := protocol.NewCodec(buf) 68 | 69 | rsp, err := rpcDataPackagecodec.Receive() 70 | So(err, ShouldBeNil) 71 | mc := rsp.GetMagicCode() 72 | So(string(magicCode), ShouldEqual, string(mc)) 73 | }) 74 | 75 | } 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 6 | 7 |

baidurpc

8 | 9 |

10 | baidurpc是一种基于TCP协议的二进制高性能RPC通信协议实现。它以Protobuf作为基本的数据交换格式。 11 | 本版本基于golang实现.完全兼容jprotobuf-rpc-socket: https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 12 |

13 | 14 | [![Go Report Card](https://goreportcard.com/badge/github.com/baidu-golang/pbrpc?style=flat-square)](https://goreportcard.com/report/github.com/baidu-golang/pbrpc) 15 | [![Go](https://github.com/baidu-golang/pbrpc/actions/workflows/main.yml/badge.svg?branch=v1.3.x)](https://github.com/baidu-golang/pbrpc/actions/workflows/main.yml) 16 | [![codecov](https://codecov.io/gh/baidu-golang/pbrpc/branch/master/graph/badge.svg?token=EY9Z88E82P)](https://codecov.io/gh/baidu-golang/pbrpc) 17 | [![Releases](https://img.shields.io/github/release/baidu-golang/pbrpc/all.svg?style=flat-square)](https://github.com/baidu-golang/pbrpc/releases) 18 | [![Go Reference](https://golang.com.cn/badge/github.com/baidu-golang/pbrpc.svg)](https://golang.com.cn/github.com/baidu-golang/pbrpc) 19 | [![LICENSE](https://img.shields.io/github/license/baidu-golang/pbrpc.svg?style=flat-square)](https://github.com/baidu-golang/pbrpc/blob/master/LICENSE) 20 | 21 | 22 | ### features: 23 | 24 | - 内置连接池,具备更高的性能,低延迟 QPS: 5w+ 25 | - 支持自动重连功能[Done] 26 | - 支持附件发送[Done] 27 | - 支持超时功能[Done] 28 | - 压缩功能,支持GZip与Snappy[Done] 29 | - 集成内置HTTP管理功能[TODO] 30 | - Client支持Ha的负载均衡功能[Done] 31 | - 灵活的超时设置功能[Done] 基于[timewheel](https://github.com/jhunters/timewheel)实现 32 | - 分包chunk支持,针对大数据包支持拆分包的发送的功能[Done] 33 | - 支持Web管理能力以及内置能力[Done] [查看](https://github.com/jhunters/brpcweb) 34 | - 支持同步发布为Http JSON协议[Done] [>= v1.2.0] 35 | ​ 36 | ### Installing 37 | 38 | To start using pbrpc, install Go and run `go get`: 39 | 40 | ```sh 41 | $ go get github.com/baidu-golang/pbrpc 42 | ``` 43 | 44 | ### Which version 45 | |version | protobuf package | 46 | | ---- | ---- | 47 | |<= 1.2.x| github.com/golang/protobuf| 48 | |1.3.x| google.golang.org/protobuf| 49 | 50 | FYI: 由于这两个pb类库并不是完全兼容,官方推荐使用 google.golang.org/protobuf 51 | 52 | ### 使用说明与Demo 53 | 54 | [Quick Start(服务发布)](./docs/quickstart_server.md)
55 | [Quick Start(客户端调用)](./docs/quickstart_client.md)
56 | [同步发布http rpc服务](./docs/httprpc.md)
57 | [更多特性使用说明](./docs/Demo.md)
58 | [Demo开发示例代码](./example)
59 | ## License 60 | brpc is [Apache 2.0 licensed](./LICENSE). 61 | -------------------------------------------------------------------------------- /benchmark_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: Malin Xie 3 | * @Description: 4 | * @Date: 2021-08-25 19:33:37 5 | */ 6 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 7 | // 8 | // Copyright 2002-2007 the original author or authors. 9 | // 10 | // Licensed under the Apache License, Version 2.0 (the "License"); 11 | // you may not use this file except in compliance with the License. 12 | // You may obtain a copy of the License at 13 | // 14 | // http://www.apache.org/licenses/LICENSE-2.0 15 | // 16 | // Unless required by applicable law or agreed to in writing, software 17 | // distributed under the License is distributed on an "AS IS" BASIS, 18 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | // See the License for the specific language governing permissions and 20 | // limitations under the License. 21 | /* 22 | * @Author: Malin Xie 23 | * @Description: 24 | * @Date: 2021-08-03 15:52:08 25 | */ 26 | package baidurpc_test 27 | 28 | import ( 29 | "testing" 30 | "time" 31 | 32 | baidurpc "github.com/baidu-golang/pbrpc" 33 | "google.golang.org/protobuf/proto" 34 | ) 35 | 36 | // BenchmarkTestLocalConnectionPerformance bench mark test 37 | func BenchmarkTestLocalConnectionPerformance(b *testing.B) { 38 | host := "localhost" 39 | port := PORT_1 40 | 41 | tcpServer := startRpcServer(0) 42 | defer stopRpcServer(tcpServer) 43 | 44 | conn, client, _ := createClient() 45 | defer client.Close() 46 | defer conn.Close() 47 | 48 | url := baidurpc.URL{} 49 | url.SetHost(&host).SetPort(&port) 50 | 51 | timeout := time.Second * 5 52 | // create client by simple connection 53 | connection, err := baidurpc.NewTCPConnection(url, &timeout) 54 | if err != nil { 55 | return 56 | } 57 | defer connection.Close() 58 | for i := 0; i < b.N; i++ { 59 | doSimpleRPCInvokeWithSignature(client, "EchoService", "echo") 60 | } 61 | 62 | } 63 | 64 | // doSimpleRPCInvokeWithSignature send rpc request 65 | func doSimpleRPCInvokeWithSignature(rpcClient *baidurpc.RpcClient, serviceName, methodName string) { 66 | rpcInvocation := baidurpc.NewRpcInvocation(&serviceName, &methodName) 67 | 68 | name := "马林" 69 | dm := DataMessage{Name: name} 70 | 71 | rpcInvocation.SetParameterIn(&dm) 72 | rpcInvocation.LogId = proto.Int64(1) 73 | 74 | parameterOut := DataMessage{} 75 | 76 | rpcClient.SendRpcRequest(rpcInvocation, ¶meterOut) 77 | } 78 | -------------------------------------------------------------------------------- /header.go: -------------------------------------------------------------------------------- 1 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 2 | // 3 | // Copyright 2002-2007 the original author or authors. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | package baidurpc 17 | 18 | import ( 19 | "bytes" 20 | "encoding/binary" 21 | ) 22 | 23 | const ( 24 | SIZE = 12 25 | 26 | MagicSize = 4 27 | 28 | MAGIC_CODE = "PRPC" 29 | 30 | COMPRESS_NO int32 = 0 31 | COMPRESS_SNAPPY int32 = 1 32 | COMPRESS_GZIP int32 = 2 33 | ) 34 | 35 | // Writable is the interface that do serialize to []byte 36 | // if errror ocurres should return non-nil error 37 | type Writable interface { 38 | Write() ([]byte, error) 39 | } 40 | 41 | // Readable is the interface that deserialize from []byte 42 | // if errror ocurres should return non-nil error 43 | type Readable interface { 44 | Read(bytes []byte) error 45 | } 46 | 47 | // RPC header content 48 | type Header struct { 49 | MagicCode []byte 50 | MessageSize int32 51 | MetaSize int32 52 | } 53 | 54 | // EmptyHead return a empty head with default value 55 | func EmptyHead() *Header { 56 | h := Header{} 57 | h.MagicCode = []byte(MAGIC_CODE) 58 | h.MessageSize = 0 59 | h.MetaSize = 0 60 | return &h 61 | } 62 | 63 | /* 64 | Convert Header struct to byte array 65 | */ 66 | func (h *Header) Write() ([]byte, error) { 67 | b := new(bytes.Buffer) 68 | 69 | binary.Write(b, binary.BigEndian, h.GetMagicCode()) 70 | binary.Write(b, binary.BigEndian, intToBytes(h.GetMessageSize())) 71 | binary.Write(b, binary.BigEndian, intToBytes(h.GetMetaSize())) 72 | return b.Bytes(), nil 73 | } 74 | 75 | func intToBytes(i int32) []byte { 76 | bytes := make([]byte, MagicSize) 77 | binary.BigEndian.PutUint32(bytes, uint32(i)) 78 | return bytes 79 | } 80 | 81 | // Read read byte array 82 | func (h *Header) Read(bytes []byte) error { 83 | if bytes == nil || len(bytes) != SIZE { 84 | return nil 85 | } 86 | h.MagicCode = bytes[0:MagicSize] 87 | // message size offset 4 and 8 88 | h.MessageSize = int32(binary.BigEndian.Uint32(bytes[4:8])) 89 | // meta size offset 8 and 12 90 | h.MetaSize = int32(binary.BigEndian.Uint32(bytes[8:12])) 91 | return nil 92 | } 93 | 94 | func (h *Header) SetMagicCode(MagicCode []byte) { 95 | if MagicCode == nil || len(MagicCode) != MagicSize { 96 | return 97 | } 98 | h.MagicCode = MagicCode 99 | } 100 | 101 | func (h *Header) GetMagicCode() []byte { 102 | return h.MagicCode 103 | } 104 | 105 | func (h *Header) SetMessageSize(MessageSize int32) { 106 | h.MessageSize = MessageSize 107 | } 108 | 109 | func (h *Header) GetMessageSize() int32 { 110 | return h.MessageSize 111 | } 112 | 113 | func (h *Header) SetMetaSize(MetaSize int32) { 114 | h.MetaSize = MetaSize 115 | } 116 | 117 | func (h *Header) GetMetaSize() int32 { 118 | return h.MetaSize 119 | } 120 | -------------------------------------------------------------------------------- /status_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: Malin Xie 3 | * @Description: 4 | * @Date: 2021-08-04 10:43:27 5 | */ 6 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 7 | // 8 | // Copyright 2002-2007 the original author or authors. 9 | // 10 | // Licensed under the Apache License, Version 2.0 (the "License"); 11 | // you may not use this file except in compliance with the License. 12 | // You may obtain a copy of the License at 13 | // 14 | // http://www.apache.org/licenses/LICENSE-2.0 15 | // 16 | // Unless required by applicable law or agreed to in writing, software 17 | // distributed under the License is distributed on an "AS IS" BASIS, 18 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | // See the License for the specific language governing permissions and 20 | // limitations under the License. 21 | /* 22 | * @Author: Malin Xie 23 | * @Description: 24 | * @Date: 2021-08-04 10:43:27 25 | */ 26 | package baidurpc_test 27 | 28 | import ( 29 | "fmt" 30 | "reflect" 31 | "testing" 32 | "time" 33 | 34 | baidurpc "github.com/baidu-golang/pbrpc" 35 | . "github.com/smartystreets/goconvey/convey" 36 | "google.golang.org/protobuf/proto" 37 | ) 38 | 39 | const ( 40 | Default_Timeout = 10 * time.Second 41 | ) 42 | 43 | // TestRpcStatus 44 | func TestRpcStatus(t *testing.T) { 45 | Convey("TestRpcStatus", t, func() { 46 | tcpServer := startRpcServer(0) 47 | defer stopRpcServer(tcpServer) 48 | 49 | serviceName := baidurpc.RPC_STATUS_SERVICENAME 50 | methodName := "Status" 51 | 52 | parameterOut := &baidurpc.RPCStatus{} 53 | err := sendRpc("localhost", PORT_1, serviceName, methodName, nil, parameterOut) 54 | So(err, ShouldBeNil) 55 | So(parameterOut.Port, ShouldEqual, PORT_1) 56 | echoservice := new(EchoService) 57 | tt := reflect.TypeOf(echoservice) 58 | 59 | So(len(parameterOut.Methods), ShouldEqual, tt.NumMethod()+2) 60 | 61 | }) 62 | } 63 | 64 | // TestRpcRequestStatus 65 | func TestRpcRequestStatus(t *testing.T) { 66 | Convey("TestRpcStatus", t, func() { 67 | tcpServer := startRpcServer(0) 68 | defer stopRpcServer(tcpServer) 69 | 70 | serviceName := baidurpc.RPC_STATUS_SERVICENAME 71 | methodName := "QpsDataStatus" 72 | 73 | parameterOut := &baidurpc.QpsData{} 74 | parameterIn := &baidurpc.RPCMethod{Service: baidurpc.RPC_STATUS_SERVICENAME, Method: methodName} 75 | err := sendRpc("localhost", PORT_1, serviceName, methodName, parameterIn, parameterOut) 76 | So(err, ShouldBeNil) 77 | 78 | }) 79 | } 80 | 81 | func sendRpc(host string, port int, serviceName, methodName string, parameterIn, parameterOut proto.Message) error { 82 | url := baidurpc.URL{} 83 | url.SetHost(&host).SetPort(&port) 84 | 85 | timeout := Default_Timeout 86 | connection, err := baidurpc.NewTCPConnection(url, &timeout) 87 | if err != nil { 88 | return err 89 | } 90 | defer connection.Close() 91 | 92 | // create client 93 | rpcClient, err := baidurpc.NewRpcCient(connection) 94 | if err != nil { 95 | return err 96 | } 97 | 98 | rpcInvocation := baidurpc.NewRpcInvocation(&serviceName, &methodName) 99 | if parameterIn != nil { 100 | rpcInvocation.SetParameterIn(parameterIn) 101 | } 102 | 103 | rpcDataPackage, err := rpcClient.SendRpcRequest(rpcInvocation, parameterOut) 104 | if int(rpcDataPackage.Meta.GetResponse().ErrorCode) == baidurpc.ST_SERVICE_NOTFOUND { 105 | return fmt.Errorf("remote server not support this feature, please upgrade version") 106 | } 107 | 108 | return err 109 | } 110 | -------------------------------------------------------------------------------- /haclient_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: Malin Xie 3 | * @Description: 4 | * @Date: 2021-04-26 18:18:59 5 | */ 6 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 7 | // 8 | // Copyright 2002-2007 the original author or authors. 9 | // 10 | // Licensed under the Apache License, Version 2.0 (the "License"); 11 | // you may not use this file except in compliance with the License. 12 | // You may obtain a copy of the License at 13 | // 14 | // http://www.apache.org/licenses/LICENSE-2.0 15 | // 16 | // Unless required by applicable law or agreed to in writing, software 17 | // distributed under the License is distributed on an "AS IS" BASIS, 18 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | // See the License for the specific language governing permissions and 20 | // limitations under the License. 21 | package baidurpc_test 22 | 23 | import ( 24 | "testing" 25 | "time" 26 | 27 | baidurpc "github.com/baidu-golang/pbrpc" 28 | . "github.com/smartystreets/goconvey/convey" 29 | "google.golang.org/protobuf/proto" 30 | ) 31 | 32 | // TestHaClient test ha client 33 | func TestHaClient(t *testing.T) { 34 | Convey("TestSingleTcpConnectionClient", t, func() { 35 | tcpServer := startRpcServer(0) 36 | defer stopRpcServer(tcpServer) 37 | 38 | host := "localhost" 39 | timeout := time.Second * 5 40 | errPort := 100 41 | urls := []baidurpc.URL{{Host: &host, Port: &errPort}, {Host: &host, Port: Int(PORT_1)}} 42 | 43 | connections, err := baidurpc.NewBatchTCPConnection(urls, timeout) 44 | So(err, ShouldBeNil) 45 | defer baidurpc.CloseBatchConnection(connections) 46 | 47 | haClient, err := baidurpc.NewHaRpcCient(connections) 48 | So(err, ShouldBeNil) 49 | So(haClient, ShouldNotBeNil) 50 | defer haClient.Close() 51 | 52 | Convey("Test send request EchoService!echo", func() { 53 | doHaSimpleRPCInvokeWithSignatureWithConvey(haClient, "EchoService", "echo", false, false, false, false) 54 | }) 55 | Convey("Test send request async EchoService!echo", func() { 56 | doHaSimpleRPCInvokeWithSignatureWithConvey(haClient, "EchoService", "echo", false, false, true, false) 57 | }) 58 | 59 | }) 60 | } 61 | 62 | // doSimpleRPCInvokeWithSignatureWithConvey send rpc request 63 | func doHaSimpleRPCInvokeWithSignatureWithConvey(rpcClient *baidurpc.HaRpcClient, serviceName, methodName string, withAttachement, withCustomErr, async, timeout bool) { 64 | Convey("Test Client send rpc request", func() { 65 | rpcInvocation := baidurpc.NewRpcInvocation(&serviceName, &methodName) 66 | 67 | name := "马林" 68 | dm := DataMessage{Name: name} 69 | 70 | rpcInvocation.SetParameterIn(&dm) 71 | rpcInvocation.LogId = proto.Int64(1) 72 | 73 | if withAttachement { 74 | rpcInvocation.Attachment = []byte("This is attachment data") 75 | } 76 | 77 | parameterOut := DataMessage{} 78 | var response *baidurpc.RpcDataPackage 79 | var err error 80 | if async { 81 | response, err = rpcClient.SendRpcRequestWithTimeout(1*time.Second, rpcInvocation, ¶meterOut) 82 | if timeout { 83 | So(err, ShouldNotBeNil) 84 | return 85 | } 86 | } else { 87 | response, err = rpcClient.SendRpcRequest(rpcInvocation, ¶meterOut) 88 | } 89 | if withCustomErr { 90 | So(err, ShouldNotBeNil) 91 | return 92 | } else { 93 | So(err, ShouldBeNil) 94 | } 95 | So(response, ShouldNotBeNil) 96 | expect := "hello " + name 97 | So(expect, ShouldEqual, parameterOut.Name) 98 | 99 | if withAttachement { 100 | So(string(response.Attachment), ShouldEqual, "I am a attachementThis is attachment data") 101 | } 102 | 103 | }) 104 | 105 | } 106 | -------------------------------------------------------------------------------- /echoservice_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: Malin Xie 3 | * @Description: 4 | * @Date: 2021-08-03 14:08:57 5 | */ 6 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 7 | // 8 | // Copyright 2002-2007 the original author or authors. 9 | // 10 | // Licensed under the Apache License, Version 2.0 (the "License"); 11 | // you may not use this file except in compliance with the License. 12 | // You may obtain a copy of the License at 13 | // 14 | // http://www.apache.org/licenses/LICENSE-2.0 15 | // 16 | // Unless required by applicable law or agreed to in writing, software 17 | // distributed under the License is distributed on an "AS IS" BASIS, 18 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | // See the License for the specific language governing permissions and 20 | // limitations under the License. 21 | /* 22 | * @Author: Malin Xie 23 | * @Description: 24 | * @Date: 2021-08-03 14:08:57 25 | */ 26 | package baidurpc_test 27 | 28 | import ( 29 | "context" 30 | "fmt" 31 | "time" 32 | 33 | baidurpc "github.com/baidu-golang/pbrpc" 34 | ) 35 | 36 | // EchoService define service 37 | type EchoService struct { 38 | } 39 | 40 | // Echo test publish method with return type has context argument 41 | func (rpc *EchoService) Echo(c context.Context, in *DataMessage) (*DataMessage, context.Context) { 42 | var ret = "hello " 43 | 44 | if len(in.Name) == 0 { 45 | ret = ret + "veryone" 46 | } else { 47 | ret = ret + in.Name 48 | } 49 | 50 | // return result 51 | dm := DataMessage{Name: ret} 52 | 53 | // time.Sleep(50 * time.Second) 54 | 55 | // bind with err 56 | // cc = baidurpc.BindError(cc, errors.New("manule error")) 57 | return &dm, context.TODO() 58 | } 59 | 60 | // Echo test publish method with return type has context argument 61 | func (rpc *EchoService) EchoWithAttchement(c context.Context, in *DataMessage) (*DataMessage, context.Context) { 62 | var ret = "hello " 63 | 64 | // get attchement 65 | attachement := baidurpc.Attachement(c) 66 | 67 | if len(in.Name) == 0 { 68 | ret = ret + "veryone" 69 | } else { 70 | ret = ret + in.Name 71 | } 72 | 73 | // return result 74 | dm := DataMessage{Name: ret} 75 | 76 | att := "I am a attachement, " + string(attachement) 77 | 78 | // bind attachment 79 | cc := baidurpc.BindAttachement(context.Background(), []byte(att)) 80 | return &dm, cc 81 | } 82 | 83 | // Echo test publish method with return type has context argument 84 | func (rpc *EchoService) EchoWithCustomizedError(c context.Context, in *DataMessage) (*DataMessage, context.Context) { 85 | var ret = "hello " 86 | 87 | if len(in.Name) == 0 { 88 | ret = ret + "veryone" 89 | } else { 90 | ret = ret + in.Name 91 | } 92 | 93 | // return result 94 | dm := DataMessage{Name: ret} 95 | 96 | // bind with err 97 | cc := baidurpc.BindError(context.Background(), fmt.Errorf("this is customized error")) 98 | return &dm, cc 99 | } 100 | 101 | // Echo test publish method with return type has context argument 102 | func (rpc *EchoService) EchoWithoutContext(c context.Context, in *DataMessage) *DataMessage { 103 | var ret = "hello " 104 | 105 | if len(in.Name) == 0 { 106 | ret = ret + "veryone" 107 | } else { 108 | ret = ret + in.Name 109 | } 110 | 111 | // return result 112 | dm := DataMessage{Name: ret} 113 | 114 | return &dm 115 | } 116 | 117 | // Echo test publish method with return type has context argument 118 | func (rpc *EchoService) EchoSlowTest(c context.Context, in *DataMessage) *DataMessage { 119 | var ret = "hello " 120 | 121 | time.Sleep(2 * time.Second) 122 | if len(in.Name) == 0 { 123 | ret = ret + "veryone" 124 | } else { 125 | ret = ret + in.Name 126 | } 127 | 128 | // return result 129 | dm := DataMessage{Name: ret} 130 | 131 | return &dm 132 | } 133 | -------------------------------------------------------------------------------- /example/client_example_test.go: -------------------------------------------------------------------------------- 1 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 2 | // 3 | // Copyright 2002-2007 the original author or authors. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | /* 17 | * @Author: Malin Xie 18 | * @Description: 19 | * @Date: 2021-08-03 19:12:14 20 | */ 21 | package example 22 | 23 | import ( 24 | "fmt" 25 | "time" 26 | 27 | baidurpc "github.com/baidu-golang/pbrpc" 28 | pool "github.com/jolestar/go-commons-pool/v2" 29 | "google.golang.org/protobuf/proto" 30 | ) 31 | 32 | // ExampleTcpClient 33 | func ExampleTcpClient() { 34 | 35 | host := "localhost" 36 | port := 1031 37 | 38 | url := baidurpc.URL{} 39 | url.SetHost(&host).SetPort(&port) 40 | 41 | timeout := time.Second * 5 42 | // create client by connection pool 43 | connection, err := baidurpc.NewTCPConnection(url, &timeout) 44 | if err != nil { 45 | return 46 | } 47 | defer connection.Close() 48 | 49 | // create client 50 | rpcClient, err := baidurpc.NewRpcCient(connection) 51 | if err != nil { 52 | return 53 | } 54 | defer rpcClient.Close() 55 | 56 | serviceName := "echoService" 57 | methodName := "echo" 58 | rpcInvocation := baidurpc.NewRpcInvocation(&serviceName, &methodName) 59 | 60 | message := "say hello from xiemalin中文测试" 61 | dm := DataMessage{Name: message} 62 | 63 | rpcInvocation.SetParameterIn(&dm) 64 | rpcInvocation.LogId = proto.Int64(1) 65 | rpcInvocation.Attachment = []byte("hello world") 66 | 67 | parameterOut := DataMessage{} 68 | 69 | response, err := rpcClient.SendRpcRequest(rpcInvocation, ¶meterOut) 70 | if err != nil { 71 | return 72 | } 73 | 74 | if response == nil { 75 | fmt.Println("Reponse is nil") 76 | return 77 | } 78 | 79 | fmt.Println("attachement", response.Attachment) 80 | 81 | } 82 | 83 | // ExampleTcpClient 84 | func ExamplePooledTcpClient() { 85 | 86 | host := "localhost" 87 | port := 1031 88 | 89 | url := baidurpc.URL{} 90 | url.SetHost(&host).SetPort(&port) 91 | 92 | timeout := time.Second * 5 93 | 94 | parell := 2 95 | 96 | // create client by connection pool 97 | config := pool.NewDefaultPoolConfig() 98 | config.MaxTotal = parell 99 | config.MaxIdle = parell 100 | 101 | // create client by connection pool 102 | connection, err := baidurpc.NewTCPConnectionPool(url, &timeout, config) 103 | if err != nil { 104 | return 105 | } 106 | defer connection.Close() 107 | 108 | // create client 109 | rpcClient, err := baidurpc.NewRpcCient(connection) 110 | if err != nil { 111 | return 112 | } 113 | defer rpcClient.Close() 114 | 115 | serviceName := "echoService" 116 | methodName := "echo" 117 | rpcInvocation := baidurpc.NewRpcInvocation(&serviceName, &methodName) 118 | 119 | message := "say hello from xiemalin中文测试" 120 | dm := DataMessage{Name: message} 121 | 122 | rpcInvocation.SetParameterIn(&dm) 123 | rpcInvocation.LogId = proto.Int64(1) 124 | rpcInvocation.Attachment = []byte("hello world") 125 | 126 | parameterOut := DataMessage{} 127 | 128 | response, err := rpcClient.SendRpcRequest(rpcInvocation, ¶meterOut) 129 | if err != nil { 130 | return 131 | } 132 | 133 | if response == nil { 134 | fmt.Println("Reponse is nil") 135 | return 136 | } 137 | 138 | fmt.Println("attachement", response.Attachment) 139 | 140 | } 141 | -------------------------------------------------------------------------------- /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/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= 5 | github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= 6 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 7 | github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= 8 | github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 9 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 10 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 11 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 12 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 13 | github.com/jhunters/goassist v1.0.9 h1:YBkUxjVFiyYWchWP1gR/zXAx7NZIUggW600ppuKNxXo= 14 | github.com/jhunters/goassist v1.0.9/go.mod h1:xswAjXTwkXnAa/qoO0F47I6AFuFPIh77ywU0TumWLUc= 15 | github.com/jhunters/link v0.0.0-20221207123848-6322292415b2 h1:rb4UVfWWSBGu84ytzQxSrmeHK0DXCnEAfAvlKw3bk7M= 16 | github.com/jhunters/link v0.0.0-20221207123848-6322292415b2/go.mod h1:4kSwKnKlQVeqldrX+9V3Zoiu9Pc0Wd4KoAD766GbolY= 17 | github.com/jhunters/timewheel v1.1.0 h1:3iNVpT9C/0sejAM+URlQS8dgvipwmkxkZWyILKH0Al8= 18 | github.com/jhunters/timewheel v1.1.0/go.mod h1:vpcvhzktGWnQuL9x+zGA32JP4ZybJEV44cRCWY/TYQ8= 19 | github.com/jolestar/go-commons-pool/v2 v2.1.1 h1:KrbCEvx5KhwcHzLTWIE8SJJQL7zzNto5in+wnO9/gSA= 20 | github.com/jolestar/go-commons-pool/v2 v2.1.1/go.mod h1:kTOzcguO2zUoEd+BySdg7Xhk/YE0HEr2bAHdWDkhMXg= 21 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 22 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 23 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 24 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 25 | github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= 26 | github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= 27 | github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= 28 | github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= 29 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 30 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 31 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 32 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 33 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 34 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 35 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 36 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 37 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 38 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 39 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 40 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 41 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 42 | google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= 43 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 44 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 45 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 46 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 47 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 48 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 49 | -------------------------------------------------------------------------------- /httpserver_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: Malin Xie 3 | * @Description: 4 | * @Date: 2021-08-20 17:16:06 5 | */ 6 | package baidurpc_test 7 | 8 | import ( 9 | "bytes" 10 | "encoding/json" 11 | "io" 12 | "io/ioutil" 13 | "net/http" 14 | "net/url" 15 | "strconv" 16 | "testing" 17 | 18 | baidurpc "github.com/baidu-golang/pbrpc" 19 | . "github.com/smartystreets/goconvey/convey" 20 | ) 21 | 22 | // TestHttpRpcServerWithPath 23 | func TestHttpRpcServerWithPath(t *testing.T) { 24 | Convey("test http mode with service not found", t, func() { 25 | 26 | tcpServer := startRpcServerWithHttpMode(0, true) 27 | defer stopRpcServer(tcpServer) 28 | 29 | Convey("test http mode with bad prefix path", func() { 30 | sport := strconv.Itoa(PORT_1) 31 | urlpath := "http://localhost:" + sport + "/badpath/EchoService/echo" 32 | paramters := map[string]string{} 33 | headers := map[string]string{} 34 | 35 | result := sendHttpRequest(urlpath, "POST", "{\"name\":\"matt\"}", paramters, headers) 36 | So(result, ShouldNotBeNil) 37 | data := &baidurpc.ResponseData{} 38 | err := json.Unmarshal(result, data) 39 | So(err, ShouldBeNil) 40 | So(data.ErrNo, ShouldEqual, baidurpc.ST_SERVICE_NOTFOUND) 41 | }) 42 | 43 | Convey("test http mode with bad service and method path", func() { 44 | sport := strconv.Itoa(PORT_1) 45 | urlpath := "http://localhost:" + sport + "/rpc/BadService/BadMethod" 46 | paramters := map[string]string{} 47 | headers := map[string]string{} 48 | 49 | result := sendHttpRequest(urlpath, "POST", "{\"name\":\"matt\"}", paramters, headers) 50 | So(result, ShouldNotBeNil) 51 | data := &baidurpc.ResponseData{} 52 | err := json.Unmarshal(result, data) 53 | So(err, ShouldBeNil) 54 | So(data.ErrNo, ShouldEqual, baidurpc.ST_SERVICE_NOTFOUND) 55 | }) 56 | 57 | }) 58 | } 59 | 60 | // TestHttpRpcServer 61 | func TestHttpRpcServer(t *testing.T) { 62 | Convey("test http rpc with common request", t, func() { 63 | 64 | tcpServer := startRpcServerWithHttpMode(0, true) 65 | defer stopRpcServer(tcpServer) 66 | 67 | sport := strconv.Itoa(PORT_1) 68 | urlpath := "http://localhost:" + sport + "/rpc/EchoService/echo" 69 | paramters := map[string]string{} 70 | headers := map[string]string{} 71 | 72 | result := sendHttpRequest(urlpath, "POST", "{\"name\":\"matt\"}", paramters, headers) 73 | 74 | So(result, ShouldNotBeNil) 75 | data := &baidurpc.ResponseData{} 76 | err := json.Unmarshal(result, data) 77 | So(err, ShouldBeNil) 78 | So(data.ErrNo, ShouldEqual, 0) 79 | 80 | }) 81 | } 82 | 83 | // TestHttpRpcServerWithAuthenticate 84 | func TestHttpRpcServerWithAuthenticate(t *testing.T) { 85 | Convey("test http rpc with authenticate", t, func() { 86 | 87 | tcpServer := startRpcServerWithHttpMode(0, true) 88 | tcpServer.SetAuthService(new(StringMatchAuthService)) 89 | tcpServer.SetTraceService(new(AddOneTraceService)) 90 | defer stopRpcServer(tcpServer) 91 | 92 | sport := strconv.Itoa(PORT_1) 93 | urlpath := "http://localhost:" + sport + "/rpc/EchoService/echo" 94 | paramters := map[string]string{} 95 | headers := map[string]string{} 96 | headers[baidurpc.Auth_key] = AUTH_TOKEN 97 | headers[baidurpc.LogId_key] = "1" 98 | 99 | headers[baidurpc.Trace_Id_key] = "1" 100 | headers[baidurpc.Trace_Span_key] = "2" 101 | headers[baidurpc.Trace_Parent_key] = "3" 102 | 103 | result := sendHttpRequest(urlpath, "POST", "{\"name\":\"matt\"}", paramters, headers) 104 | 105 | So(result, ShouldNotBeNil) 106 | data := &baidurpc.ResponseData{} 107 | err := json.Unmarshal(result, data) 108 | So(err, ShouldBeNil) 109 | So(data.ErrNo, ShouldEqual, 0) 110 | 111 | }) 112 | } 113 | 114 | func sendHttpRequest(urlpath, method, body string, paramters, headers map[string]string) []byte { 115 | params := url.Values{} 116 | Url, err := url.Parse(urlpath) 117 | if err != nil { 118 | return nil 119 | } 120 | 121 | for k, v := range paramters { 122 | params.Set(k, v) 123 | } 124 | 125 | urlPath := Url.String() 126 | 127 | client := &http.Client{} 128 | req, _ := http.NewRequest(method, urlPath, nil) 129 | 130 | data := []byte(body) 131 | bs := bytes.NewBuffer(data) 132 | req.ContentLength = int64(len(data)) 133 | // buf := body.Bytes() 134 | req.GetBody = func() (io.ReadCloser, error) { 135 | r := bytes.NewReader(data) 136 | return ioutil.NopCloser(r), nil 137 | } 138 | 139 | req.Body = ioutil.NopCloser(bs) 140 | 141 | for k, v := range headers { 142 | req.Header.Add(k, v) 143 | } 144 | 145 | resp, _ := client.Do(req) 146 | rbody, _ := ioutil.ReadAll(resp.Body) 147 | 148 | return rbody 149 | } 150 | -------------------------------------------------------------------------------- /datamessage_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go v1.26.0 4 | // protoc v3.9.2 5 | // source: DataMessage.proto 6 | 7 | package baidurpc_test 8 | 9 | import ( 10 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 11 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 12 | reflect "reflect" 13 | sync "sync" 14 | ) 15 | 16 | const ( 17 | // Verify that this generated code is sufficiently up-to-date. 18 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 19 | // Verify that runtime/protoimpl is sufficiently up-to-date. 20 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 21 | ) 22 | 23 | type DataMessage struct { 24 | state protoimpl.MessageState 25 | sizeCache protoimpl.SizeCache 26 | unknownFields protoimpl.UnknownFields 27 | 28 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 29 | } 30 | 31 | func (x *DataMessage) Reset() { 32 | *x = DataMessage{} 33 | if protoimpl.UnsafeEnabled { 34 | mi := &file_DataMessage_proto_msgTypes[0] 35 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 36 | ms.StoreMessageInfo(mi) 37 | } 38 | } 39 | 40 | func (x *DataMessage) String() string { 41 | return protoimpl.X.MessageStringOf(x) 42 | } 43 | 44 | func (*DataMessage) ProtoMessage() {} 45 | 46 | func (x *DataMessage) ProtoReflect() protoreflect.Message { 47 | mi := &file_DataMessage_proto_msgTypes[0] 48 | if protoimpl.UnsafeEnabled && x != nil { 49 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 50 | if ms.LoadMessageInfo() == nil { 51 | ms.StoreMessageInfo(mi) 52 | } 53 | return ms 54 | } 55 | return mi.MessageOf(x) 56 | } 57 | 58 | // Deprecated: Use DataMessage.ProtoReflect.Descriptor instead. 59 | func (*DataMessage) Descriptor() ([]byte, []int) { 60 | return file_DataMessage_proto_rawDescGZIP(), []int{0} 61 | } 62 | 63 | func (x *DataMessage) GetName() string { 64 | if x != nil { 65 | return x.Name 66 | } 67 | return "" 68 | } 69 | 70 | var File_DataMessage_proto protoreflect.FileDescriptor 71 | 72 | var file_DataMessage_proto_rawDesc = []byte{ 73 | 0x0a, 0x11, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 74 | 0x6f, 0x74, 0x6f, 0x22, 0x21, 0x0a, 0x0b, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x73, 0x61, 75 | 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 76 | 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x1f, 0x5a, 0x1d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 77 | 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x61, 0x69, 0x64, 0x75, 0x2d, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 78 | 0x67, 0x2f, 0x70, 0x62, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 79 | } 80 | 81 | var ( 82 | file_DataMessage_proto_rawDescOnce sync.Once 83 | file_DataMessage_proto_rawDescData = file_DataMessage_proto_rawDesc 84 | ) 85 | 86 | func file_DataMessage_proto_rawDescGZIP() []byte { 87 | file_DataMessage_proto_rawDescOnce.Do(func() { 88 | file_DataMessage_proto_rawDescData = protoimpl.X.CompressGZIP(file_DataMessage_proto_rawDescData) 89 | }) 90 | return file_DataMessage_proto_rawDescData 91 | } 92 | 93 | var file_DataMessage_proto_msgTypes = make([]protoimpl.MessageInfo, 1) 94 | var file_DataMessage_proto_goTypes = []interface{}{ 95 | (*DataMessage)(nil), // 0: DataMessage 96 | } 97 | var file_DataMessage_proto_depIdxs = []int32{ 98 | 0, // [0:0] is the sub-list for method output_type 99 | 0, // [0:0] is the sub-list for method input_type 100 | 0, // [0:0] is the sub-list for extension type_name 101 | 0, // [0:0] is the sub-list for extension extendee 102 | 0, // [0:0] is the sub-list for field type_name 103 | } 104 | 105 | func init() { file_DataMessage_proto_init() } 106 | func file_DataMessage_proto_init() { 107 | if File_DataMessage_proto != nil { 108 | return 109 | } 110 | if !protoimpl.UnsafeEnabled { 111 | file_DataMessage_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 112 | switch v := v.(*DataMessage); i { 113 | case 0: 114 | return &v.state 115 | case 1: 116 | return &v.sizeCache 117 | case 2: 118 | return &v.unknownFields 119 | default: 120 | return nil 121 | } 122 | } 123 | } 124 | type x struct{} 125 | out := protoimpl.TypeBuilder{ 126 | File: protoimpl.DescBuilder{ 127 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 128 | RawDescriptor: file_DataMessage_proto_rawDesc, 129 | NumEnums: 0, 130 | NumMessages: 1, 131 | NumExtensions: 0, 132 | NumServices: 0, 133 | }, 134 | GoTypes: file_DataMessage_proto_goTypes, 135 | DependencyIndexes: file_DataMessage_proto_depIdxs, 136 | MessageInfos: file_DataMessage_proto_msgTypes, 137 | }.Build() 138 | File_DataMessage_proto = out.File 139 | file_DataMessage_proto_rawDesc = nil 140 | file_DataMessage_proto_goTypes = nil 141 | file_DataMessage_proto_depIdxs = nil 142 | } 143 | -------------------------------------------------------------------------------- /example/pb_example_test.go: -------------------------------------------------------------------------------- 1 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 2 | // 3 | // Copyright 2002-2007 the original author or authors. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | /* 17 | * @Author: Malin Xie 18 | * @Description: 19 | * @Date: 2021-08-03 19:24:19 20 | */ 21 | package example 22 | 23 | import ( 24 | reflect "reflect" 25 | sync "sync" 26 | 27 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 28 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 29 | ) 30 | 31 | const ( 32 | // Verify that this generated code is sufficiently up-to-date. 33 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 34 | // Verify that runtime/protoimpl is sufficiently up-to-date. 35 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 36 | ) 37 | 38 | type DataMessage struct { 39 | state protoimpl.MessageState 40 | sizeCache protoimpl.SizeCache 41 | unknownFields protoimpl.UnknownFields 42 | 43 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 44 | } 45 | 46 | func (x *DataMessage) Reset() { 47 | *x = DataMessage{} 48 | if protoimpl.UnsafeEnabled { 49 | mi := &file_DataMessage_proto_msgTypes[0] 50 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 51 | ms.StoreMessageInfo(mi) 52 | } 53 | } 54 | 55 | func (x *DataMessage) String() string { 56 | return protoimpl.X.MessageStringOf(x) 57 | } 58 | 59 | func (*DataMessage) ProtoMessage() {} 60 | 61 | func (x *DataMessage) ProtoReflect() protoreflect.Message { 62 | mi := &file_DataMessage_proto_msgTypes[0] 63 | if protoimpl.UnsafeEnabled && x != nil { 64 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 65 | if ms.LoadMessageInfo() == nil { 66 | ms.StoreMessageInfo(mi) 67 | } 68 | return ms 69 | } 70 | return mi.MessageOf(x) 71 | } 72 | 73 | // Deprecated: Use DataMessage.ProtoReflect.Descriptor instead. 74 | func (*DataMessage) Descriptor() ([]byte, []int) { 75 | return file_DataMessage_proto_rawDescGZIP(), []int{0} 76 | } 77 | 78 | func (x *DataMessage) GetName() string { 79 | if x != nil { 80 | return x.Name 81 | } 82 | return "" 83 | } 84 | 85 | var File_DataMessage_proto protoreflect.FileDescriptor 86 | 87 | var file_DataMessage_proto_rawDesc = []byte{ 88 | 0x0a, 0x11, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 89 | 0x6f, 0x74, 0x6f, 0x22, 0x21, 0x0a, 0x0b, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x73, 0x61, 90 | 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 91 | 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x1f, 0x5a, 0x1d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 92 | 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x61, 0x69, 0x64, 0x75, 0x2d, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 93 | 0x67, 0x2f, 0x70, 0x62, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 94 | } 95 | 96 | var ( 97 | file_DataMessage_proto_rawDescOnce sync.Once 98 | file_DataMessage_proto_rawDescData = file_DataMessage_proto_rawDesc 99 | ) 100 | 101 | func file_DataMessage_proto_rawDescGZIP() []byte { 102 | file_DataMessage_proto_rawDescOnce.Do(func() { 103 | file_DataMessage_proto_rawDescData = protoimpl.X.CompressGZIP(file_DataMessage_proto_rawDescData) 104 | }) 105 | return file_DataMessage_proto_rawDescData 106 | } 107 | 108 | var file_DataMessage_proto_msgTypes = make([]protoimpl.MessageInfo, 1) 109 | var file_DataMessage_proto_goTypes = []interface{}{ 110 | (*DataMessage)(nil), // 0: DataMessage 111 | } 112 | var file_DataMessage_proto_depIdxs = []int32{ 113 | 0, // [0:0] is the sub-list for method output_type 114 | 0, // [0:0] is the sub-list for method input_type 115 | 0, // [0:0] is the sub-list for extension type_name 116 | 0, // [0:0] is the sub-list for extension extendee 117 | 0, // [0:0] is the sub-list for field type_name 118 | } 119 | 120 | func init() { file_DataMessage_proto_init() } 121 | func file_DataMessage_proto_init() { 122 | if File_DataMessage_proto != nil { 123 | return 124 | } 125 | if !protoimpl.UnsafeEnabled { 126 | file_DataMessage_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 127 | switch v := v.(*DataMessage); i { 128 | case 0: 129 | return &v.state 130 | case 1: 131 | return &v.sizeCache 132 | case 2: 133 | return &v.unknownFields 134 | default: 135 | return nil 136 | } 137 | } 138 | } 139 | type x struct{} 140 | out := protoimpl.TypeBuilder{ 141 | File: protoimpl.DescBuilder{ 142 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 143 | RawDescriptor: file_DataMessage_proto_rawDesc, 144 | NumEnums: 0, 145 | NumMessages: 1, 146 | NumExtensions: 0, 147 | NumServices: 0, 148 | }, 149 | GoTypes: file_DataMessage_proto_goTypes, 150 | DependencyIndexes: file_DataMessage_proto_depIdxs, 151 | MessageInfos: file_DataMessage_proto_msgTypes, 152 | }.Build() 153 | File_DataMessage_proto = out.File 154 | file_DataMessage_proto_rawDesc = nil 155 | file_DataMessage_proto_goTypes = nil 156 | file_DataMessage_proto_depIdxs = nil 157 | } 158 | -------------------------------------------------------------------------------- /server_test.go: -------------------------------------------------------------------------------- 1 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 2 | // 3 | // Copyright 2002-2007 the original author or authors. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | package baidurpc_test 17 | 18 | import ( 19 | "context" 20 | "errors" 21 | "testing" 22 | "time" 23 | 24 | baidurpc "github.com/baidu-golang/pbrpc" 25 | . "github.com/smartystreets/goconvey/convey" 26 | "google.golang.org/protobuf/proto" 27 | ) 28 | 29 | const ( 30 | PORT_1 = 1031 31 | PORT_2 = 1032 32 | PORT_3 = 1033 33 | 34 | Shutdown_Timeout = time.Second 35 | ) 36 | 37 | // createRpcServer create rpc server by port and localhost 38 | func createRpcServer(port int) *baidurpc.TcpServer { 39 | serverMeta := baidurpc.ServerMeta{} 40 | serverMeta.Port = Int(port) 41 | rpcServer := baidurpc.NewTpcServer(&serverMeta) 42 | return rpcServer 43 | } 44 | 45 | type CustomAuthService struct { 46 | } 47 | 48 | // Authenticate 49 | func (as *CustomAuthService) Authenticate(service, name string, authToken []byte) bool { 50 | return true 51 | } 52 | 53 | // TestServerWithAuthenticate 54 | func TestServerWithAuthenticate(t *testing.T) { 55 | Convey("TestServerWithoutPublishMethods", t, func() { 56 | 57 | rpcServer := createRpcServer(PORT_2) 58 | authservice := new(CustomAuthService) 59 | rpcServer.SetAuthService(authservice) 60 | err := rpcServer.Start() 61 | So(err, ShouldBeNil) 62 | stopRpcServer(rpcServer) 63 | So(err, ShouldBeNil) 64 | }) 65 | } 66 | 67 | // TestServerWithoutPublishMethods 68 | func TestServerWithoutPublishMethods(t *testing.T) { 69 | Convey("TestServerWithoutPublishMethods", t, func() { 70 | 71 | rpcServer := createRpcServer(PORT_2) 72 | err := rpcServer.Start() 73 | So(err, ShouldBeNil) 74 | stopRpcServer(rpcServer) 75 | So(err, ShouldBeNil) 76 | }) 77 | } 78 | 79 | // TestServerWithPublishMethods 80 | func TestServerWithPublishMethods(t *testing.T) { 81 | Convey("TestServerWithoutPublishMethods", t, func() { 82 | 83 | Convey("publish method with RegisterName", func() { 84 | rpcServer := createRpcServer(PORT_2) 85 | 86 | echoservice := new(EchoService) 87 | rpcServer.RegisterName("EchoService", echoservice) 88 | 89 | err := rpcServer.Start() 90 | So(err, ShouldBeNil) 91 | stopRpcServer(rpcServer) 92 | So(err, ShouldBeNil) 93 | }) 94 | 95 | Convey("publish method with RegisterNameWithMethodMapping", func() { 96 | rpcServer := createRpcServer(PORT_2) 97 | 98 | echoservice := new(EchoService) 99 | methodMapping := map[string]string{ 100 | "Echo": "echo", 101 | "EchoWithAttchement": "echoWithAttchement", 102 | "EchoWithCustomizedError": "echoWithCustomizedError", 103 | "EchoWithoutContext": "echoWithoutContext", 104 | } 105 | rpcServer.RegisterNameWithMethodMapping("EchoService", echoservice, methodMapping) 106 | 107 | err := rpcServer.Start() 108 | So(err, ShouldBeNil) 109 | stopRpcServer(rpcServer) 110 | So(err, ShouldBeNil) 111 | }) 112 | 113 | }) 114 | } 115 | 116 | // SimpleService 117 | type SimpleService struct { 118 | serviceName string 119 | methodName string 120 | } 121 | 122 | // NewSimpleService create RPC service 123 | func NewSimpleService(serviceName, methodName string) *SimpleService { 124 | ret := SimpleService{serviceName, methodName} 125 | return &ret 126 | } 127 | 128 | func (ss *SimpleService) DoService(msg proto.Message, attachment []byte, logId *int64) (proto.Message, []byte, error) { 129 | var ret = "hello " 130 | 131 | if msg != nil { 132 | 133 | var name string 134 | 135 | m, ok := msg.(*DataMessage) 136 | if !ok { 137 | errStr := "message type is not type of 'DataMessage'" 138 | return nil, nil, errors.New(errStr) 139 | } 140 | name = m.Name 141 | 142 | if len(name) == 0 { 143 | ret = ret + "veryone" 144 | } else { 145 | ret = ret + name 146 | } 147 | } 148 | dm := DataMessage{} 149 | dm.Name = ret 150 | return &dm, []byte{1, 5, 9}, nil 151 | 152 | } 153 | 154 | func (ss *SimpleService) GetServiceName() string { 155 | return ss.serviceName 156 | } 157 | 158 | func (ss *SimpleService) GetMethodName() string { 159 | return ss.methodName 160 | } 161 | 162 | func (ss *SimpleService) NewParameter() proto.Message { 163 | ret := DataMessage{} 164 | return &ret 165 | } 166 | 167 | // TestServerWithOldRegisterWay 168 | func TestServerWithOldRegisterWay(t *testing.T) { 169 | 170 | Convey("TestServerWithOldRegisterWay", t, func() { 171 | rpcServer := createRpcServer(PORT_3) 172 | So(rpcServer, ShouldNotBeNil) 173 | 174 | service := NewSimpleService("OldService", "OldMethod") 175 | rpcServer.Register(service) 176 | 177 | err := rpcServer.Start() 178 | So(err, ShouldBeNil) 179 | stopRpcServer(rpcServer) 180 | So(err, ShouldBeNil) 181 | }) 182 | 183 | } 184 | 185 | func stopRpcServer(rpcServer *baidurpc.TcpServer) { 186 | timeContext, _ := context.WithTimeout(context.Background(), Shutdown_Timeout) 187 | rpcServer.Stop(timeContext) 188 | } 189 | 190 | // Int convert to pointer type of int 191 | func Int(v int) *int { 192 | p := new(int) 193 | *p = int(v) 194 | return p 195 | } 196 | -------------------------------------------------------------------------------- /example/server_example_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: Malin Xie 3 | * @Description: 4 | * @Date: 2021-08-03 19:30:12 5 | */ 6 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 7 | // 8 | // Copyright 2002-2007 the original author or authors. 9 | // 10 | // Licensed under the Apache License, Version 2.0 (the "License"); 11 | // you may not use this file except in compliance with the License. 12 | // You may obtain a copy of the License at 13 | // 14 | // http://www.apache.org/licenses/LICENSE-2.0 15 | // 16 | // Unless required by applicable law or agreed to in writing, software 17 | // distributed under the License is distributed on an "AS IS" BASIS, 18 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | // See the License for the specific language governing permissions and 20 | // limitations under the License. 21 | /* 22 | * @Author: Malin Xie 23 | * @Description: 24 | * @Date: 2021-08-03 19:12:14 25 | */ 26 | package example 27 | 28 | import ( 29 | "context" 30 | "errors" 31 | "fmt" 32 | "log" 33 | "net" 34 | "net/rpc" 35 | "reflect" 36 | "strings" 37 | "time" 38 | 39 | baidurpc "github.com/baidu-golang/pbrpc" 40 | "google.golang.org/protobuf/proto" 41 | ) 42 | 43 | const ( 44 | Shutdown_Timeout = time.Second 45 | ) 46 | 47 | // ExampleRpcServer 48 | func ExampleRpcServer() { 49 | port := 1031 50 | 51 | serverMeta := baidurpc.ServerMeta{} 52 | serverMeta.QPSExpireInSecs = 600 // set qps monitor expire time 53 | serverMeta.Port = &port 54 | rpcServer := baidurpc.NewTpcServer(&serverMeta) 55 | 56 | echoService := new(EchoService) 57 | 58 | mapping := make(map[string]string) 59 | mapping["Echo"] = "echo" 60 | rpcServer.RegisterNameWithMethodMapping("echoService", echoService, mapping) 61 | 62 | rpcServer.Start() 63 | defer stopRpcServer(rpcServer) 64 | 65 | } 66 | 67 | func ExampleRpcServerWithHttp() { 68 | port := 1031 69 | 70 | serverMeta := baidurpc.ServerMeta{} 71 | serverMeta.QPSExpireInSecs = 600 // set qps monitor expire time 72 | serverMeta.Port = &port 73 | rpcServer := baidurpc.NewTpcServer(&serverMeta) 74 | 75 | echoService := new(EchoService) 76 | 77 | mapping := make(map[string]string) 78 | mapping["Echo"] = "echo" 79 | rpcServer.RegisterNameWithMethodMapping("echoService", echoService, mapping) 80 | 81 | // start http rpc mode 82 | rpcServer.EnableHttp() 83 | rpcServer.Start() 84 | defer stopRpcServer(rpcServer) 85 | 86 | } 87 | 88 | // ExampleRpcServerWithListener 89 | func ExampleRpcServerWithListener() { 90 | serverMeta := baidurpc.ServerMeta{} 91 | serverMeta.QPSExpireInSecs = 600 // set qps monitor expire time 92 | rpcServer := baidurpc.NewTpcServer(&serverMeta) 93 | 94 | echoService := new(EchoService) 95 | 96 | mapping := make(map[string]string) 97 | mapping["Echo"] = "echo" 98 | rpcServer.RegisterNameWithMethodMapping("echoService", echoService, mapping) 99 | 100 | addr := ":1031" 101 | listener, err := net.Listen("tcp", addr) 102 | if err != nil { 103 | return 104 | } 105 | rpcServer.StartServer(listener) 106 | defer stopRpcServer(rpcServer) 107 | 108 | } 109 | 110 | // ExampleRpcServerWithListener 111 | func ExampleRpcServerRegisterWithCallback() { 112 | serverMeta := baidurpc.ServerMeta{} 113 | serverMeta.QPSExpireInSecs = 600 // set qps monitor expire time 114 | rpcServer := baidurpc.NewTpcServer(&serverMeta) 115 | 116 | callback := func(msg proto.Message, attachment []byte, logId *int64) (proto.Message, []byte, error) { 117 | var ret = "hello " 118 | 119 | if msg != nil { 120 | t := reflect.TypeOf(msg) 121 | 122 | if !strings.Contains(t.String(), "DataMessage") { 123 | errStr := "message type is not type of 'DataMessage'" + t.String() 124 | return nil, nil, errors.New(errStr) 125 | } 126 | 127 | var name string 128 | 129 | m := msg.(*DataMessage) 130 | name = m.Name 131 | 132 | if len(name) == 0 { 133 | ret = ret + "veryone" 134 | } else { 135 | ret = ret + name 136 | } 137 | } 138 | dm := DataMessage{} 139 | dm.Name = ret 140 | return &dm, []byte{1, 5, 9}, nil 141 | } 142 | 143 | rpcServer.RegisterRpc("echoService", "echo", callback, &DataMessage{}) 144 | 145 | rpcServer.Start() 146 | defer stopRpcServer(rpcServer) 147 | 148 | } 149 | 150 | type EchoService struct { 151 | } 152 | 153 | // Echo test publish method with return type has context argument 154 | func (rpc *EchoService) Echo(c context.Context, in *DataMessage) (*DataMessage, context.Context) { 155 | var ret = "hello " 156 | 157 | attachement := baidurpc.Attachement(c) 158 | fmt.Println("attachement", attachement) 159 | 160 | if len(in.Name) == 0 { 161 | ret = ret + "veryone" 162 | } else { 163 | ret = ret + in.Name 164 | } 165 | dm := DataMessage{} 166 | dm.Name = ret 167 | 168 | // bind attachment 169 | cc := baidurpc.BindAttachement(context.Background(), []byte("hello")) 170 | // bind with err 171 | // cc = baidurpc.BindError(cc, errors.New("manule error")) 172 | return &dm, cc 173 | } 174 | 175 | type HelloService struct{} 176 | 177 | func (p *HelloService) Hello(request string, reply *string) error { 178 | *reply = "hello:" + request 179 | return nil 180 | } 181 | 182 | func DoRPCServer() { 183 | fmt.Println("server starting...") 184 | rpc.RegisterName("HelloService", new(HelloService)) // 注册RPC, RPC名称为 HelloService 185 | 186 | listener, err := net.Listen("tcp", ":1234") 187 | if err != nil { 188 | log.Fatal("ListenTCP error:", err) 189 | } 190 | 191 | conn, err := listener.Accept() 192 | if err != nil { 193 | log.Fatal("Accept error:", err) 194 | } 195 | 196 | rpc.ServeConn(conn) // 绑定tcp服务链接 197 | 198 | fmt.Println("server started.") 199 | } 200 | 201 | func stopRpcServer(rpcServer *baidurpc.TcpServer) { 202 | timeContext, _ := context.WithTimeout(context.Background(), Shutdown_Timeout) 203 | rpcServer.Stop(timeContext) 204 | } 205 | -------------------------------------------------------------------------------- /status.go: -------------------------------------------------------------------------------- 1 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 2 | // 3 | // Copyright 2002-2007 the original author or authors. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | /* 17 | * @Author: Malin Xie 18 | * @Description: 19 | * @Date: 2021-07-26 17:09:25 20 | */ 21 | package baidurpc 22 | 23 | import ( 24 | "context" 25 | "encoding/json" 26 | "fmt" 27 | "time" 28 | 29 | "github.com/jhunters/timewheel" 30 | ) 31 | 32 | // HttpStatusView 33 | type HttpStatusView struct { 34 | server *TcpServer 35 | } 36 | 37 | func (hsv *HttpStatusView) Status(c context.Context) (*RPCStatus, context.Context) { 38 | s := hsv.server 39 | result := &RPCStatus{} 40 | if s.serverMeta.Host != nil { 41 | result.Host = *s.serverMeta.Host 42 | } 43 | if s.serverMeta.Port != nil { 44 | result.Port = int32(*s.serverMeta.Port) 45 | } 46 | if s.serverMeta.IdleTimeoutSeconds != nil { 47 | result.TimeoutSenconds = int32(*s.serverMeta.IdleTimeoutSeconds) 48 | } 49 | 50 | rpcServices := s.services 51 | methods := make([]*RPCMethod, len(rpcServices)) 52 | var i int = 0 53 | for sname, service := range rpcServices { 54 | m := &RPCMethod{Service: service.GetServiceName(), Method: service.GetMethodName()} 55 | // query meta info 56 | serviceMeta, ok := s.servicesMeta[sname] 57 | if ok { 58 | if serviceMeta.InPbFieldMetas != nil { 59 | metaString, _ := json.Marshal(serviceMeta.InPbFieldMetas) 60 | m.InTypeMeta = string(metaString) 61 | } 62 | if serviceMeta.RetrunPbFieldMetas != nil { 63 | metaString, _ := json.Marshal(serviceMeta.RetrunPbFieldMetas) 64 | m.ReturnTypeMeta = string(metaString) 65 | } 66 | } 67 | 68 | methods[i] = m 69 | i++ 70 | } 71 | result.Methods = methods 72 | return result, c 73 | } 74 | 75 | func (hsv *HttpStatusView) QpsDataStatus(c context.Context, method *RPCMethod) (*QpsData, context.Context) { 76 | serviceId := GetServiceId(method.Service, method.Method) 77 | ret := &QpsData{Qpsinfo: make(map[int64]int32)} 78 | requestStatus, ok := hsv.server.requestStatus.Methods[serviceId] 79 | if ok { 80 | ret.Qpsinfo = requestStatus.QpsStatus 81 | } 82 | // add current current 83 | ret.Qpsinfo[time.Now().Unix()] += 0 84 | return ret, c 85 | } 86 | 87 | // RPCRequestStatus 88 | type RPCRequestStatus struct { 89 | Methods map[string]*RPCMethodReuqestStatus 90 | 91 | reqeustChan chan request 92 | 93 | closeChan chan bool 94 | 95 | expireAfterSecs int16 96 | 97 | started bool 98 | 99 | tw *timewheel.TimeWheel[request] 100 | } 101 | 102 | type request struct { 103 | method string 104 | t time.Time 105 | count int 106 | } 107 | 108 | // RPCMethodReuqestStatus 109 | type RPCMethodReuqestStatus struct { 110 | QpsStatus map[int64]int32 111 | } 112 | 113 | // NewRPCRequestStatus 114 | func NewRPCRequestStatus(services map[string]Service) *RPCRequestStatus { 115 | ret := &RPCRequestStatus{ 116 | Methods: make(map[string]*RPCMethodReuqestStatus, len(services)), 117 | reqeustChan: make(chan request, 1024), 118 | closeChan: make(chan bool), 119 | } 120 | 121 | for name := range services { 122 | ret.Methods[name] = &RPCMethodReuqestStatus{QpsStatus: make(map[int64]int32, 1024)} 123 | } 124 | 125 | return ret 126 | } 127 | 128 | // Start 129 | func (r *RPCRequestStatus) Start() error { 130 | Infof("RPC method reuqest status record starting. expire time within %d seconds ", r.expireAfterSecs) 131 | r.started = true 132 | 133 | // start time wheel to delete expire data 134 | tw, err := timewheel.New[request](1*time.Second, uint16(r.expireAfterSecs)) 135 | if err != nil { 136 | r.started = false 137 | return err 138 | } 139 | r.tw = tw 140 | r.tw.Start() 141 | 142 | for { 143 | select { 144 | case m := <-r.reqeustChan: 145 | status, ok := r.Methods[m.method] 146 | if !ok { 147 | status = &RPCMethodReuqestStatus{QpsStatus: make(map[int64]int32, 1024)} 148 | r.Methods[m.method] = status 149 | } 150 | k := m.t.Unix() 151 | count, ok := status.QpsStatus[k] 152 | if !ok { 153 | count = int32(m.count) 154 | // add task 155 | task := timewheel.Task[request]{ 156 | Data: m, 157 | TimeoutCallback: func(tt timewheel.Task[request]) { // call back function on time out 158 | k := tt.Data 159 | r.expire(k.method, k.t) 160 | 161 | }} 162 | // add task and return unique task id 163 | r.tw.AddTask(time.Duration(r.expireAfterSecs)*time.Second, task) // add delay task 164 | } else { 165 | count += int32(m.count) 166 | } 167 | status.QpsStatus[k] = count 168 | 169 | case <-r.closeChan: 170 | r.started = false 171 | return nil 172 | } 173 | } 174 | 175 | } 176 | 177 | // RequestIn 178 | func (r *RPCRequestStatus) RequestIn(methodName string, t time.Time, count int) error { 179 | if !r.started { 180 | return fmt.Errorf("RequestIn failed status not started") 181 | } 182 | req := request{method: methodName, t: t, count: count} 183 | r.reqeustChan <- req 184 | 185 | return nil 186 | } 187 | 188 | // expire remove qps data after time expire 189 | func (r *RPCRequestStatus) expire(methodName string, t time.Time) { 190 | status, ok := r.Methods[methodName] 191 | if ok { 192 | delete(status.QpsStatus, t.Unix()) 193 | } 194 | } 195 | 196 | // Stop 197 | func (r *RPCRequestStatus) Stop() { 198 | if !r.started { 199 | return 200 | } 201 | r.started = false 202 | r.closeChan <- true 203 | 204 | r.tw.Stop() 205 | } 206 | -------------------------------------------------------------------------------- /connection.go: -------------------------------------------------------------------------------- 1 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 2 | // 3 | // Copyright 2002-2007 the original author or authors. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | package baidurpc 17 | 18 | import ( 19 | "errors" 20 | "fmt" 21 | "strconv" 22 | "time" 23 | 24 | "github.com/jhunters/link" 25 | ) 26 | 27 | var ( 28 | errSessionIsNil = errors.New("[conn-001]Session is nil, maybe not init Connect() function") 29 | errInvalidUrl = errors.New("[conn-002]parameter 'url' of host property is nil") 30 | LOG_INVALID_PORT = "[conn-003]invalid parameter 'url' of port property is '%d'" 31 | ) 32 | 33 | /* 34 | Connection handler interface 35 | */ 36 | type Connection interface { 37 | SendReceive(rpcDataPackage *RpcDataPackage) (*RpcDataPackage, error) 38 | Send(rpcDataPackage *RpcDataPackage) error 39 | Receive() (*RpcDataPackage, error) 40 | Close() error 41 | Reconnect() error 42 | } 43 | 44 | type ConnectionTester interface { 45 | TestConnection() error 46 | } 47 | 48 | // TCPConnection simple tcp based connection implementation 49 | type TCPConnection struct { 50 | address string 51 | sendChanSize int 52 | session *link.Session[*RpcDataPackage, *RpcDataPackage] 53 | protocol *RpcDataPackageProtocol[*RpcDataPackage, *RpcDataPackage] 54 | } 55 | 56 | /* 57 | Create a new TCPConnection and try to connect to target server by URL. 58 | */ 59 | func NewTCPConnection(url URL, timeout *time.Duration) (*TCPConnection, error) { 60 | connection := TCPConnection{} 61 | 62 | err := connection.connect(url, timeout, 0) 63 | if err != nil { 64 | return nil, err 65 | } 66 | 67 | return &connection, nil 68 | } 69 | 70 | func (c *TCPConnection) connect(url URL, timeout *time.Duration, sendChanSize int) error { 71 | host := url.Host 72 | if host == nil || len(*host) == 0 { 73 | return errInvalidUrl 74 | } 75 | port := url.Port 76 | if port == nil || *port <= 0 { 77 | return fmt.Errorf(fmt.Sprintf(LOG_INVALID_PORT, port)) 78 | } 79 | 80 | u := *host + ":" + strconv.Itoa(*port) 81 | protocol, err := NewRpcDataPackageProtocol() 82 | if err != nil { 83 | return err 84 | } 85 | 86 | c.protocol = protocol 87 | c.protocol.timeout = timeout 88 | c.address = u 89 | c.sendChanSize = sendChanSize 90 | session, err := doConnect(u, protocol, timeout, sendChanSize) 91 | if err != nil { 92 | return err 93 | } 94 | c.session = session 95 | return nil 96 | } 97 | 98 | func doConnect[S, R *RpcDataPackage](address string, protocol *RpcDataPackageProtocol[*RpcDataPackage, *RpcDataPackage], timeout *time.Duration, sendChanSize int) (*link.Session[*RpcDataPackage, *RpcDataPackage], error) { 99 | var session *link.Session[*RpcDataPackage, *RpcDataPackage] 100 | var err error 101 | if timeout == nil { 102 | session, err = link.Dial[*RpcDataPackage, *RpcDataPackage]("tcp", address, protocol, sendChanSize) 103 | } else { 104 | session, err = link.DialTimeout[*RpcDataPackage, *RpcDataPackage]("tcp", address, *timeout, protocol, sendChanSize) 105 | } 106 | if err != nil { 107 | return nil, err 108 | } 109 | return session, nil 110 | } 111 | 112 | func (c *TCPConnection) TestConnection() error { 113 | if c.session == nil { 114 | return errSessionIsNil 115 | } 116 | closed := c.session.IsClosed() 117 | if closed { 118 | return fmt.Errorf("session closed") 119 | } 120 | return nil 121 | } 122 | 123 | func (c *TCPConnection) GetId() uint64 { 124 | if c.session != nil { 125 | return c.session.ID() 126 | } 127 | 128 | return uint64(0) 129 | } 130 | 131 | // SendReceive send data to connect and block wait data recevie 132 | func (c *TCPConnection) SendReceive(rpcDataPackage *RpcDataPackage) (*RpcDataPackage, error) { 133 | if c.session == nil { 134 | return nil, errSessionIsNil 135 | } 136 | 137 | err := c.session.Send(rpcDataPackage) 138 | 139 | if err != nil { 140 | return nil, err 141 | } 142 | 143 | return doReceive(c.session) 144 | 145 | } 146 | 147 | // Send data to connection 148 | func (c *TCPConnection) Send(rpcDataPackage *RpcDataPackage) error { 149 | if c.session == nil { 150 | return errSessionIsNil 151 | } 152 | 153 | return c.session.Send(rpcDataPackage) 154 | 155 | } 156 | 157 | // Receive data from connection 158 | func (c *TCPConnection) Receive() (*RpcDataPackage, error) { 159 | if c.session == nil { 160 | return nil, errSessionIsNil 161 | } 162 | 163 | return doReceive(c.session) 164 | } 165 | 166 | func doReceive(session *link.Session[*RpcDataPackage, *RpcDataPackage]) (rpcDataPackage *RpcDataPackage, err error) { 167 | rsp, err := session.Receive() 168 | if err != nil { 169 | return nil, err 170 | } 171 | 172 | if rsp == nil { // receive a error data could be ignored 173 | return nil, nil 174 | } 175 | 176 | return rsp, nil 177 | 178 | } 179 | 180 | // Close close connection 181 | func (c *TCPConnection) Close() error { 182 | if c.session != nil { 183 | Info("close session id=", c.session.ID()) 184 | return c.session.Close() 185 | } 186 | 187 | if c.protocol != nil { 188 | c.protocol.Stop() 189 | } 190 | return nil 191 | } 192 | 193 | // Reconnect do connect by saved info 194 | func (c *TCPConnection) Reconnect() error { 195 | Info("try to reconnect to server", c.address) 196 | session, err := doConnect(c.address, c.protocol, c.protocol.timeout, c.sendChanSize) 197 | if err != nil { 198 | return err 199 | } 200 | Info("reconnect success to server", c.address) 201 | c.session = session 202 | return nil 203 | } 204 | -------------------------------------------------------------------------------- /connectionpool.go: -------------------------------------------------------------------------------- 1 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 2 | // 3 | // Copyright 2002-2007 the original author or authors. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | package baidurpc 17 | 18 | import ( 19 | "context" 20 | "errors" 21 | "time" 22 | 23 | pool "github.com/jolestar/go-commons-pool/v2" 24 | ) 25 | 26 | var ( 27 | Empty_Head = make([]byte, SIZE) 28 | 29 | errPoolNotInit = errors.New("[connpool-001]Object pool is nil maybe not init Connect() function") 30 | errGetConnFail = errors.New("[connpool-002]Can not get connection from connection pool. target object is nil") 31 | errDestroyObjectNil = errors.New("[connpool-003]Destroy object failed due to target object is nil") 32 | 33 | HB_SERVICE_NAME = "__heartbeat" 34 | HB_METHOD_NAME = "__beat" 35 | ) 36 | 37 | /* 38 | type Connection interface { 39 | SendReceive(rpcDataPackage *RpcDataPackage) (*RpcDataPackage, error) 40 | Close() error 41 | } 42 | */ 43 | 44 | type TCPConnectionPool struct { 45 | Config *pool.ObjectPoolConfig 46 | objectPool *pool.ObjectPool 47 | timeout *time.Duration 48 | } 49 | 50 | func NewDefaultTCPConnectionPool(url URL, timeout *time.Duration) (*TCPConnectionPool, error) { 51 | return NewTCPConnectionPool(url, timeout, nil) 52 | } 53 | 54 | func NewTCPConnectionPool(url URL, timeout *time.Duration, config *pool.ObjectPoolConfig) (*TCPConnectionPool, error) { 55 | connection := TCPConnectionPool{timeout: timeout} 56 | if config == nil { 57 | connection.Config = pool.NewDefaultPoolConfig() 58 | connection.Config.TestOnBorrow = true 59 | 60 | } else { 61 | connection.Config = config 62 | } 63 | 64 | err := connection.connect(url, timeout, 0) 65 | if err != nil { 66 | return nil, err 67 | } 68 | 69 | return &connection, nil 70 | } 71 | 72 | func (c *TCPConnectionPool) connect(url URL, timeout *time.Duration, sendChanSize int) error { 73 | 74 | factory := ConnectionPoolFactory{timeout: timeout} 75 | factory.url = &url 76 | 77 | var objectPool *pool.ObjectPool 78 | 79 | eConfig := c.Config 80 | if eConfig == nil { 81 | eConfig = pool.NewDefaultPoolConfig() 82 | } 83 | 84 | objectPool = pool.NewObjectPool(context.Background(), &factory, eConfig) 85 | c.objectPool = objectPool 86 | 87 | return nil 88 | 89 | } 90 | 91 | func (c *TCPConnectionPool) borrowObject() (*TCPConnection, error) { 92 | if c.objectPool == nil { 93 | return nil, errPoolNotInit 94 | } 95 | 96 | object, err := c.objectPool.BorrowObject(context.Background()) 97 | if err != nil { 98 | return nil, err 99 | } 100 | 101 | if object == nil { 102 | return nil, errGetConnFail 103 | } 104 | 105 | return object.(*TCPConnection), nil 106 | } 107 | 108 | func (c *TCPConnectionPool) SendReceive(rpcDataPackage *RpcDataPackage) (*RpcDataPackage, error) { 109 | object, err := c.borrowObject() 110 | if err != nil { 111 | return nil, err 112 | } 113 | defer c.objectPool.ReturnObject(context.Background(), object) 114 | 115 | return object.SendReceive(rpcDataPackage) 116 | 117 | } 118 | 119 | // Send 120 | func (c *TCPConnectionPool) Send(rpcDataPackage *RpcDataPackage) error { 121 | object, err := c.borrowObject() 122 | if err != nil { 123 | return err 124 | } 125 | defer c.objectPool.ReturnObject(context.Background(), object) 126 | err = object.Send(rpcDataPackage) 127 | return err 128 | 129 | } 130 | 131 | // Receive 132 | func (c *TCPConnectionPool) Receive() (*RpcDataPackage, error) { 133 | object, err := c.borrowObject() 134 | if err != nil { 135 | return nil, err 136 | } 137 | defer c.objectPool.ReturnObject(context.Background(), object) 138 | 139 | return object.Receive() 140 | } 141 | 142 | func (c *TCPConnectionPool) Close() error { 143 | if c.objectPool == nil { 144 | return errPoolNotInit 145 | } 146 | 147 | c.objectPool.Close(context.Background()) 148 | return nil 149 | } 150 | 151 | func (c *TCPConnectionPool) GetNumActive() int { 152 | if c.objectPool == nil { 153 | return 0 154 | } 155 | 156 | return c.objectPool.GetNumActive() 157 | } 158 | 159 | // Reconnect do connect by saved info 160 | func (c *TCPConnectionPool) Reconnect() error { 161 | // do nothing 162 | return nil 163 | } 164 | 165 | type ConnectionPoolFactory struct { 166 | url *URL 167 | timeout *time.Duration 168 | } 169 | 170 | func (c *ConnectionPoolFactory) MakeObject(ctx context.Context) (*pool.PooledObject, error) { 171 | if c.url == nil { 172 | return nil, errPoolNotInit 173 | } 174 | 175 | connection := TCPConnection{} 176 | err := connection.connect(*c.url, c.timeout, 0) 177 | if err != nil { 178 | return nil, err 179 | } 180 | 181 | return pool.NewPooledObject(&connection), nil 182 | } 183 | 184 | func (c *ConnectionPoolFactory) DestroyObject(ctx context.Context, object *pool.PooledObject) error { 185 | obj := object.Object 186 | if obj == nil { 187 | return errDestroyObjectNil 188 | } 189 | 190 | conn := obj.(Connection) 191 | return conn.Close() 192 | } 193 | 194 | func (c *ConnectionPoolFactory) ValidateObject(ctx context.Context, object *pool.PooledObject) bool { 195 | 196 | obj := object.Object 197 | if obj == nil { 198 | return false 199 | } 200 | 201 | conn := obj.(ConnectionTester) 202 | 203 | return conn.TestConnection() == nil 204 | } 205 | 206 | func (c *ConnectionPoolFactory) ActivateObject(ctx context.Context, object *pool.PooledObject) error { 207 | return nil 208 | } 209 | 210 | func (c *ConnectionPoolFactory) PassivateObject(ctx context.Context, object *pool.PooledObject) error { 211 | return nil 212 | } 213 | -------------------------------------------------------------------------------- /rpcpackage_test.go: -------------------------------------------------------------------------------- 1 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 2 | // 3 | // Copyright 2002-2007 the original author or authors. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | package baidurpc_test 17 | 18 | import ( 19 | "testing" 20 | 21 | baidurpc "github.com/baidu-golang/pbrpc" 22 | . "github.com/smartystreets/goconvey/convey" 23 | "google.golang.org/protobuf/proto" 24 | ) 25 | 26 | var sericeName = "thisIsAServiceName" 27 | var methodName = "thisIsAMethodName" 28 | var magicCode = "PRPC" 29 | var logId int64 = 1001 30 | var correlationId int64 = 20001 31 | var data []byte = []byte{1, 2, 3, 1, 2, 3, 1, 1, 2, 2, 20} 32 | var attachment []byte = []byte{2, 2, 2, 2, 2, 1, 1, 1, 1} 33 | 34 | func initRpcDataPackage() *baidurpc.RpcDataPackage { 35 | 36 | rpcDataPackage := baidurpc.RpcDataPackage{} 37 | 38 | rpcDataPackage.MagicCode(magicCode) 39 | rpcDataPackage.SetData(data) 40 | rpcDataPackage.ServiceName(sericeName) 41 | rpcDataPackage.MethodName(methodName) 42 | 43 | rpcDataPackage.LogId(logId) 44 | rpcDataPackage.CorrelationId(correlationId) 45 | 46 | rpcDataPackage.SetAttachment(attachment) 47 | 48 | return &rpcDataPackage 49 | } 50 | 51 | func equalRpcDataPackage(r baidurpc.RpcDataPackage, t *testing.T) error { 52 | Convey("test RpcDataPackage", func() { 53 | So(sericeName, ShouldEqual, r.Meta.Request.ServiceName) 54 | So(methodName, ShouldEqual, r.Meta.Request.MethodName) 55 | So(string(magicCode), ShouldEqual, string(r.GetMagicCode())) 56 | So(r.Meta.Request.LogId, ShouldEqual, logId) 57 | So(r.Meta.CorrelationId, ShouldEqual, correlationId) 58 | So(len(data), ShouldEqual, len(r.Data)) 59 | So(len(attachment), ShouldEqual, len(r.Attachment)) 60 | }) 61 | return nil 62 | } 63 | 64 | func validateRpcDataPackage(t *testing.T, r2 baidurpc.RpcDataPackage) { 65 | Convey("validateRpcDataPackage", func() { 66 | So(string(magicCode), ShouldEqual, string(r2.GetMagicCode())) 67 | So(sericeName, ShouldEqual, r2.GetMeta().GetRequest().GetServiceName()) 68 | So(methodName, ShouldEqual, r2.GetMeta().GetRequest().GetMethodName()) 69 | }) 70 | 71 | } 72 | 73 | // TestWriteReaderWithMockData 74 | func TestWriteReaderWithMockData(t *testing.T) { 75 | 76 | Convey("TestWriteReaderWithMockData", t, func() { 77 | rpcDataPackage := initRpcDataPackage() 78 | 79 | b, err := rpcDataPackage.Write() 80 | if err != nil { 81 | t.Error(err.Error()) 82 | } 83 | 84 | r2 := baidurpc.RpcDataPackage{} 85 | 86 | err = r2.Read(b) 87 | if err != nil { 88 | t.Error(err.Error()) 89 | } 90 | 91 | validateRpcDataPackage(t, r2) 92 | }) 93 | 94 | } 95 | 96 | // WriteReaderWithRealData 97 | func WriteReaderWithRealData(rpcDataPackage *baidurpc.RpcDataPackage, 98 | compressType int32, t *testing.T) { 99 | 100 | Convey("Test with real data", func() { 101 | dataMessage := DataMessage{} 102 | name := "hello, xiemalin. this is repeated string aaaaaaaaaaaaaaaaaaaaaa" 103 | dataMessage.Name = name 104 | 105 | data, err := proto.Marshal(&dataMessage) 106 | So(err, ShouldBeNil) 107 | rpcDataPackage.SetData(data) 108 | 109 | b, err := rpcDataPackage.Write() 110 | So(err, ShouldBeNil) 111 | 112 | r2 := baidurpc.RpcDataPackage{} 113 | r2.CompressType(compressType) 114 | 115 | err = r2.Read(b) 116 | So(err, ShouldBeNil) 117 | 118 | validateRpcDataPackage(t, r2) 119 | 120 | newData := r2.GetData() 121 | dataMessage2 := DataMessage{} 122 | proto.Unmarshal(newData, &dataMessage2) 123 | 124 | So(name, ShouldEqual, dataMessage2.Name) 125 | }) 126 | 127 | } 128 | 129 | // TestWriteReaderWithRealData 130 | func TestWriteReaderWithRealData(t *testing.T) { 131 | 132 | Convey("TestWriteReaderWithRealData", t, func() { 133 | rpcDataPackage := initRpcDataPackage() 134 | WriteReaderWithRealData(rpcDataPackage, baidurpc.COMPRESS_NO, t) 135 | }) 136 | 137 | } 138 | 139 | // TestWriteReaderWithGZIP 140 | func TestWriteReaderWithGZIP(t *testing.T) { 141 | 142 | Convey("TestWriteReaderWithGZIP", t, func() { 143 | rpcDataPackage := initRpcDataPackage() 144 | rpcDataPackage.CompressType(baidurpc.COMPRESS_GZIP) 145 | WriteReaderWithRealData(rpcDataPackage, baidurpc.COMPRESS_GZIP, t) 146 | }) 147 | 148 | } 149 | 150 | // TestWriteReaderWithSNAPPY 151 | func TestWriteReaderWithSNAPPY(t *testing.T) { 152 | 153 | Convey("TestWriteReaderWithSNAPPY", t, func() { 154 | rpcDataPackage := initRpcDataPackage() 155 | 156 | rpcDataPackage.CompressType(baidurpc.COMPRESS_SNAPPY) 157 | 158 | WriteReaderWithRealData(rpcDataPackage, baidurpc.COMPRESS_SNAPPY, t) 159 | }) 160 | 161 | } 162 | 163 | // TestChunk 164 | func TestChunk(t *testing.T) { 165 | 166 | Convey("Test package chunk", t, func() { 167 | rpcDataPackage := initRpcDataPackage() 168 | 169 | Convey("Test package chunk with invalid chunk size", func() { 170 | chunkSize := 0 171 | chunkPackages := rpcDataPackage.Chunk(chunkSize) 172 | So(len(chunkPackages), ShouldEqual, 1) 173 | }) 174 | 175 | Convey("Test package chunk with chunk size", func() { 176 | chunkSize := 1 177 | count := len(rpcDataPackage.Data) 178 | chunkPackages := rpcDataPackage.Chunk(chunkSize) 179 | So(len(chunkPackages), ShouldEqual, count) 180 | So(len(chunkPackages[0].Data), ShouldEqual, 1) 181 | }) 182 | 183 | Convey("Test package chunk with large chunk size", func() { 184 | chunkSize := 100 185 | datasize := len(rpcDataPackage.Data) 186 | chunkPackages := rpcDataPackage.Chunk(chunkSize) 187 | So(len(chunkPackages), ShouldEqual, 1) 188 | So(len(chunkPackages[0].Data), ShouldEqual, datasize) 189 | }) 190 | }) 191 | 192 | } 193 | -------------------------------------------------------------------------------- /httpserver.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: Malin Xie 3 | * @Description: 4 | * @Date: 2021-08-19 13:22:01 5 | */ 6 | package baidurpc 7 | 8 | import ( 9 | "context" 10 | "encoding/json" 11 | "fmt" 12 | "io/ioutil" 13 | "log" 14 | "net" 15 | "net/http" 16 | "strconv" 17 | "strings" 18 | ) 19 | 20 | const ( 21 | HttpRpcPath = "/rpc/" 22 | 23 | LogId_key = "X-LogID" 24 | Auth_key = "X-Authenticate" 25 | Trace_Id_key = "X-Trace_ID" 26 | Trace_Span_key = "X-Trace_Span" 27 | Trace_Parent_key = "X-Trace_Parent" 28 | Request_Meta_Key = "X-Request-Meta" // Json value 29 | ) 30 | 31 | // ResponseData 32 | type ResponseData struct { 33 | ErrNo int `json:"errno"` 34 | Message string `json:"message,omitempty"` 35 | Data interface{} `json:"data,omitempty"` 36 | } 37 | 38 | type HttpServer struct { 39 | s *TcpServer 40 | httpsrv *http.Server 41 | } 42 | 43 | func (h *HttpServer) serverHttp(l net.Listener) { 44 | 45 | srv := &http.Server{ 46 | Handler: h, 47 | } 48 | 49 | h.httpsrv = srv 50 | 51 | go func() { 52 | // service connections 53 | if err := srv.Serve(l); err != nil && err != http.ErrServerClosed { 54 | log.Fatalf("listen: %s\n", err) 55 | } 56 | }() 57 | 58 | } 59 | 60 | // ServeHTTP to serve http reqeust and response to process http rpc handle 61 | func (h *HttpServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { 62 | path := req.URL.Path 63 | if !strings.HasPrefix(path, HttpRpcPath) { 64 | data := toJson(errResponse(ST_SERVICE_NOTFOUND, fmt.Sprintf("no service or method found by path='%s'", path))) 65 | w.Write(data) 66 | return 67 | } 68 | serviceName, method, err := getServiceMethod(path) 69 | if err != nil { 70 | data := toJson(errResponse(ST_ERROR, err.Error())) 71 | w.Write(data) 72 | return 73 | } 74 | 75 | sid := GetServiceId(serviceName, method) 76 | 77 | service, ok := h.s.services[sid] 78 | if !ok { 79 | data := toJson(errResponse(ST_SERVICE_NOTFOUND, fmt.Sprintf("no service or method found by path='%s'", path))) 80 | w.Write(data) 81 | return 82 | } 83 | 84 | // authenticate 85 | if h.s.authService != nil { 86 | authData := getHeaderAsByte(req, Auth_key) 87 | authOk := h.s.authService.Authenticate(serviceName, method, authData) 88 | if !authOk { 89 | data := toJson(errResponse(ST_AUTH_ERROR, errAuth.Error())) 90 | w.Write(data) 91 | return 92 | } 93 | } 94 | 95 | if h.s.traceService != nil { 96 | traceId := getHeaderAsInt64(req, Trace_Id_key) 97 | spanId := getHeaderAsInt64(req, Trace_Span_key) 98 | parentId := getHeaderAsInt64(req, Trace_Parent_key) 99 | traceInfo := &TraceInfo{TraceId: traceId, SpanId: spanId, ParentSpanId: parentId} 100 | 101 | value := getHeaderAsByte(req, Request_Meta_Key) 102 | if value != nil { 103 | value, _ = UnescapeUnicode(value) 104 | metaExt := map[string]string{} 105 | err := json.Unmarshal(value, &metaExt) 106 | if err == nil { 107 | traceInfo.RpcRequestMetaExt = metaExt 108 | } 109 | } 110 | 111 | traceRetrun := h.s.traceService.Trace(serviceName, method, traceInfo) 112 | if traceRetrun != nil { 113 | w.Header().Set(Trace_Id_key, int64ToString(traceRetrun.TraceId)) 114 | w.Header().Set(Trace_Span_key, int64ToString(traceRetrun.SpanId)) 115 | w.Header().Set(Trace_Parent_key, int64ToString(traceRetrun.ParentSpanId)) 116 | if traceRetrun.RpcRequestMetaExt != nil { 117 | metaData, err := json.Marshal(traceRetrun.RpcRequestMetaExt) 118 | if err == nil { 119 | w.Header().Set(Request_Meta_Key, string(metaData)) 120 | } 121 | } 122 | } 123 | } 124 | 125 | // get json data 126 | jsonData, err := ioutil.ReadAll(req.Body) 127 | if err != nil { 128 | data := toJson(errResponse(ST_ERROR, err.Error())) 129 | w.Write(data) 130 | return 131 | } 132 | 133 | paramIn := service.NewParameter() 134 | err = json.Unmarshal(jsonData, paramIn) 135 | if err != nil { 136 | data := toJson(errResponse(ST_ERROR, err.Error())) 137 | w.Write(data) 138 | return 139 | } 140 | 141 | // get logid 142 | var logid int64 = getHeaderAsInt64(req, LogId_key) 143 | 144 | ec := &ErrorContext{} 145 | ret, _, err := h.s.doServiceInvoke(ec, paramIn, serviceName, method, nil, logid, service) 146 | if err != nil { 147 | data := toJson(errResponse(ST_ERROR, err.Error())) 148 | w.Write(data) 149 | return 150 | } 151 | 152 | resData := &ResponseData{ErrNo: 0, Data: ret} 153 | data, err := json.Marshal(resData) 154 | if err != nil { 155 | data := toJson(errResponse(ST_ERROR, err.Error())) 156 | w.Write(data) 157 | return 158 | } 159 | 160 | w.Write(data) 161 | 162 | } 163 | 164 | // shutdown do shutdown action to close http server 165 | func (h *HttpServer) shutdown(ctx context.Context) { 166 | if h.httpsrv != nil { 167 | h.httpsrv.Shutdown(ctx) 168 | } 169 | } 170 | 171 | func getHeaderAsByte(req *http.Request, key string) []byte { 172 | value, ok := req.Header[http.CanonicalHeaderKey(key)] 173 | if !ok || len(value) != 1 { 174 | return nil 175 | } 176 | return []byte(value[0]) 177 | } 178 | 179 | func getHeaderAsInt64(req *http.Request, key string) int64 { 180 | value, ok := req.Header[http.CanonicalHeaderKey(key)] 181 | if !ok || len(value) != 1 { 182 | return -1 183 | } 184 | 185 | id, _ := strconv.Atoi(value[0]) 186 | return int64(id) 187 | } 188 | 189 | func int64ToString(i int64) string { 190 | return strconv.Itoa(int(i)) 191 | } 192 | 193 | func errResponse(errno int, message string) *ResponseData { 194 | return &ResponseData{ErrNo: errno, Message: message} 195 | } 196 | 197 | func toJson(o interface{}) []byte { 198 | data, _ := json.Marshal(o) 199 | return data 200 | } 201 | 202 | func getServiceMethod(path string) (string, string, error) { 203 | p := strings.TrimPrefix(path, HttpRpcPath) 204 | p = strings.TrimSuffix(p, "/") 205 | 206 | seperate := strings.Split(p, "/") 207 | if len(seperate) != 2 { 208 | return "", "", fmt.Errorf("no service or method found by path='%s'", p) 209 | } 210 | return seperate[0], seperate[1], nil 211 | } 212 | 213 | // UnescapeUnicode 214 | func UnescapeUnicode(raw []byte) ([]byte, error) { 215 | str, err := strconv.Unquote(strings.Replace(strconv.Quote(string(raw)), `\\u`, `\u`, -1)) 216 | if err != nil { 217 | return nil, err 218 | } 219 | return []byte(str), nil 220 | } 221 | -------------------------------------------------------------------------------- /haclient.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: Malin Xie 3 | * @Description: 4 | * @Date: 2021-04-26 18:18:59 5 | */ 6 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 7 | // 8 | // Copyright 2002-2007 the original author or authors. 9 | // 10 | // Licensed under the Apache License, Version 2.0 (the "License"); 11 | // you may not use this file except in compliance with the License. 12 | // You may obtain a copy of the License at 13 | // 14 | // http://www.apache.org/licenses/LICENSE-2.0 15 | // 16 | // Unless required by applicable law or agreed to in writing, software 17 | // distributed under the License is distributed on an "AS IS" BASIS, 18 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | // See the License for the specific language governing permissions and 20 | // limitations under the License. 21 | package baidurpc 22 | 23 | import ( 24 | "errors" 25 | "fmt" 26 | "log" 27 | "sync" 28 | "time" 29 | 30 | "github.com/jhunters/timewheel" 31 | "google.golang.org/protobuf/proto" 32 | ) 33 | 34 | // HaRpcClient high avialbe RpcClient 35 | type HaRpcClient struct { 36 | rpcClients []*RpcClient 37 | 38 | current int32 39 | 40 | locker sync.Mutex 41 | 42 | tw *timewheel.TimeWheel[*RpcDataPackage] 43 | } 44 | 45 | // NewBatchTCPConnection to create batch connection 46 | func NewBatchTCPConnection(urls []URL, timeout time.Duration) ([]Connection, error) { 47 | if len(urls) == 0 { 48 | return nil, errors.New("param 'urls' is empty") 49 | } 50 | 51 | var result []Connection 52 | for _, url := range urls { 53 | conn, err := NewTCPConnection(url, &timeout) 54 | if err != nil { 55 | log.Println("create connection failed:", err) 56 | continue 57 | } 58 | result = append(result, conn) 59 | } 60 | 61 | return result, nil 62 | } 63 | 64 | // CloseBatchConnection close batch connections 65 | func CloseBatchConnection(connections []Connection) { 66 | for _, conn := range connections { 67 | conn.Close() 68 | } 69 | } 70 | 71 | // NewHaRpcCient 72 | func NewHaRpcCient(connections []Connection) (*HaRpcClient, error) { 73 | return NewHaRpcCientWithTimewheelSetting(connections, defaultTimewheelInterval, uint16(defaultTimewheelSlot)) 74 | } 75 | 76 | // NewHaRpcCient 77 | func NewHaRpcCientWithTimewheelSetting(connections []Connection, timewheelInterval time.Duration, timewheelSlot uint16) (*HaRpcClient, error) { 78 | if len(connections) == 0 { 79 | return nil, errors.New("param 'connections' is empty") 80 | } 81 | 82 | rpcClients := make([]*RpcClient, len(connections)) 83 | for idx, connection := range connections { 84 | rpcClient, err := NewRpcCient(connection) 85 | if err != nil { 86 | return nil, err 87 | } 88 | rpcClients[idx] = rpcClient 89 | } 90 | result := &HaRpcClient{rpcClients: rpcClients, current: 0} 91 | result.tw, _ = timewheel.New[*RpcDataPackage](timewheelInterval, timewheelSlot) 92 | result.tw.Start() 93 | return result, nil 94 | } 95 | 96 | // electClient 97 | func (c *HaRpcClient) electClient() *RpcClient { 98 | if len(c.rpcClients) == 0 { 99 | return nil 100 | } 101 | 102 | c.locker.Lock() 103 | defer c.locker.Unlock() 104 | 105 | client := c.rpcClients[int(c.current)%len(c.rpcClients)] 106 | c.current++ 107 | return client 108 | } 109 | 110 | // SendRpcRequest send rpc request by elect one client 111 | func (c *HaRpcClient) SendRpcRequest(rpcInvocation *RpcInvocation, responseMessage proto.Message) (*RpcDataPackage, error) { 112 | var errRet error 113 | size := len(c.rpcClients) 114 | if size == 0 { 115 | return nil, errors.New("no rpc client avaible") 116 | } 117 | 118 | for i := 0; i < size; i++ { 119 | rpcClient := c.electClient() 120 | if rpcClient == nil { 121 | return nil, errors.New("no rpc client avaible") 122 | } 123 | 124 | data, err := rpcClient.SendRpcRequest(rpcInvocation, responseMessage) 125 | if err == nil { 126 | return data, nil 127 | } else { 128 | errRet = err 129 | } 130 | } 131 | 132 | return nil, errRet 133 | } 134 | 135 | // SendRpcRequest send rpc request by elect one client with timeout feature 136 | func (c *HaRpcClient) SendRpcRequestWithTimeout(timeout time.Duration, rpcInvocation *RpcInvocation, responseMessage proto.Message) (*RpcDataPackage, error) { 137 | var errRet error 138 | size := len(c.rpcClients) 139 | if size == 0 { 140 | return nil, errors.New("no rpc client avaible") 141 | } 142 | 143 | ch := make(chan *RpcDataPackage) 144 | go c.asyncRequest(timeout, rpcInvocation, responseMessage, ch) 145 | defer close(ch) 146 | // wait for message 147 | rsp := <-ch 148 | 149 | return rsp, errRet 150 | } 151 | 152 | // asyncRequest 153 | func (c *HaRpcClient) asyncRequest(timeout time.Duration, rpcInvocation *RpcInvocation, responseMessage proto.Message, ch chan<- *RpcDataPackage) { 154 | request, err := rpcInvocation.GetRequestRpcDataPackage() 155 | if err != nil { 156 | errorcode := int32(ST_ERROR) 157 | request.ErrorCode(errorcode) 158 | errormsg := err.Error() 159 | request.ErrorText(errormsg) 160 | 161 | ch <- request 162 | return 163 | } 164 | 165 | // create a task bind with key, data and time out call back function. 166 | t := &timewheel.Task[*RpcDataPackage]{ 167 | Data: nil, // business data 168 | TimeoutCallback: func(task timewheel.Task[*RpcDataPackage]) { // call back function on time out 169 | // process someting after time out happened. 170 | errorcode := int32(ST_READ_TIMEOUT) 171 | request.ErrorCode(errorcode) 172 | errormsg := fmt.Sprintf("request time out of %v", task.Delay()) 173 | request.ErrorText(errormsg) 174 | ch <- request 175 | }} 176 | 177 | // add task and return unique task id 178 | taskid, err := c.tw.AddTask(timeout, *t) // add delay task 179 | if err != nil { 180 | errorcode := int32(ST_ERROR) 181 | request.ErrorCode(errorcode) 182 | errormsg := err.Error() 183 | request.ErrorText(errormsg) 184 | 185 | ch <- request 186 | return 187 | } 188 | 189 | defer func() { 190 | c.tw.RemoveTask(taskid) 191 | if e := recover(); e != nil { 192 | Warningf("asyncRequest failed with error %v", e) 193 | } 194 | }() 195 | 196 | rsp, err := c.SendRpcRequest(rpcInvocation, responseMessage) 197 | if err != nil { 198 | errorcode := int32(ST_ERROR) 199 | request.ErrorCode(errorcode) 200 | errormsg := err.Error() 201 | request.ErrorText(errormsg) 202 | 203 | ch <- request 204 | return 205 | } 206 | 207 | ch <- rsp 208 | } 209 | 210 | // Close do close all client 211 | func (c *HaRpcClient) Close() { 212 | if c.tw != nil { 213 | c.tw.Stop() 214 | } 215 | for _, client := range c.rpcClients { 216 | client.Close() 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /codec.go: -------------------------------------------------------------------------------- 1 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 2 | // 3 | // Copyright 2002-2007 the original author or authors. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | package baidurpc 17 | 18 | import ( 19 | "errors" 20 | "io" 21 | "log" 22 | "net" 23 | "time" 24 | 25 | "github.com/jhunters/link" 26 | "github.com/jhunters/timewheel" 27 | ) 28 | 29 | const REQUIRED_TYPE = "baidurpc.RpcDataPackage" 30 | 31 | var ( 32 | LOG_CLOSE_CONNECT_INFO = "[codec-100]Do close connection. connection info:%v" 33 | chunkPackageCacheExpire = 60 * time.Second 34 | ) 35 | 36 | /* 37 | Codec implements for RpcDataPackage. 38 | */ 39 | type RpcDataPackageCodec[S, R *RpcDataPackage] struct { 40 | readWriter io.ReadWriter 41 | closer io.Closer 42 | p *RpcDataPackageProtocol[S, R] 43 | timeout *time.Duration 44 | 45 | chunkPackageCache map[int64]*RpcDataPackage 46 | } 47 | 48 | // Here begin to implements link module Codec interface for RpcDataPackageCodec 49 | /* 50 | type Codec[S, R any] interface { 51 | Receive() (R, error) 52 | Send(S) error 53 | Close() error 54 | } 55 | */ 56 | 57 | // send serialized data to target server by connection IO 58 | // msg: param 'msg' must type of RpcDataPackage 59 | func (r *RpcDataPackageCodec[S, R]) Send(dataPackage *RpcDataPackage) error { 60 | if dataPackage == nil { 61 | return errors.New("parameter 'msg' is nil") 62 | } 63 | 64 | rw := r.readWriter 65 | if r.timeout != nil { 66 | conn := rw.(net.Conn) 67 | conn.SetWriteDeadline(time.Now().Add(*r.timeout)) 68 | } 69 | 70 | // check if use chunk mode 71 | chunkSize := dataPackage.chunkSize 72 | if chunkSize > 0 { 73 | dataPackageList := dataPackage.Chunk(int(chunkSize)) 74 | for _, pack := range dataPackageList { 75 | err := pack.WriteIO(rw) 76 | if err != nil { 77 | return err 78 | } 79 | } 80 | } else { 81 | err := dataPackage.WriteIO(rw) 82 | if err != nil { 83 | return err 84 | } 85 | } 86 | return nil 87 | } 88 | 89 | // receive serialized data to target server by connection IO 90 | // return param: 91 | // 1. RpcDataPackage unserialized from connection io. or nil if exception found 92 | // 2. a non-nil error if any io exception occurred 93 | func (r *RpcDataPackageCodec[S, R]) Receive() (*RpcDataPackage, error) { 94 | 95 | rw := r.readWriter 96 | 97 | if r.timeout != nil { // set time out 98 | conn := rw.(net.Conn) 99 | conn.SetReadDeadline(time.Now().Add(*r.timeout)) 100 | } 101 | 102 | return r.doReceive(rw) 103 | 104 | } 105 | 106 | func (r *RpcDataPackageCodec[S, R]) doReceive(conn io.ReadWriter) (*RpcDataPackage, error) { 107 | dataPackage := dataPackagePool.Get() 108 | dataPackage.Clear() 109 | err := dataPackage.ReadIO(conn) 110 | if err != nil { 111 | if err == errIgnoreErr { 112 | return nil, nil 113 | } 114 | return nil, err 115 | } 116 | 117 | // if chunk mode enabled 118 | if r.p.chunkSize > 0 { 119 | dataPackage.chunkSize = r.p.chunkSize 120 | } 121 | 122 | // check if chunk package 123 | if dataPackage.IsChunkPackage() { 124 | streamId := dataPackage.GetChunkStreamId() 125 | 126 | cachedPackage, exist := r.chunkPackageCache[streamId] 127 | if !exist { 128 | r.chunkPackageCache[streamId] = dataPackage 129 | cachedPackage = dataPackage 130 | 131 | // add task 132 | task := timewheel.Task[int64]{ 133 | Data: streamId, 134 | TimeoutCallback: func(tt timewheel.Task[int64]) { // call back function on time out 135 | k := tt.Data 136 | delete(r.chunkPackageCache, k) 137 | }} 138 | // add task and return unique task id 139 | r.p.tw.AddTask(chunkPackageCacheExpire, task) // add delay task 140 | 141 | } else { 142 | // if exist should merge data 143 | size := len(cachedPackage.Data) + len(dataPackage.Data) 144 | newData := make([]byte, size) 145 | copy(newData, cachedPackage.Data) 146 | copy(newData[len(cachedPackage.Data):], dataPackage.Data) 147 | cachedPackage.Data = newData 148 | r.chunkPackageCache[streamId] = cachedPackage 149 | } 150 | 151 | if dataPackage.IsFinalPackage() { 152 | delete(r.chunkPackageCache, streamId) 153 | // clear chunk status 154 | cachedPackage.ClearChunkStatus() 155 | return cachedPackage, nil 156 | } else { 157 | return r.doReceive(conn) // to receive next chunk package 158 | } 159 | } 160 | 161 | return dataPackage, nil 162 | } 163 | 164 | // do close connection io 165 | // return non-nil if any error ocurred while doing close 166 | func (r *RpcDataPackageCodec[S, R]) Close() error { 167 | if r.closer != nil { 168 | log.Printf(LOG_CLOSE_CONNECT_INFO, r.closer) 169 | return r.closer.Close() 170 | } 171 | return nil 172 | } 173 | 174 | // set connection io read and write dead line 175 | func (r *RpcDataPackageCodec[S, R]) SetTimeout(timeout *time.Duration) { 176 | r.timeout = timeout 177 | } 178 | 179 | // Here begin to implements link module Protocol interface for RpcDataPackageCodec 180 | /* 181 | type Protocol[S, R any] interface { 182 | NewCodec(rw io.ReadWriter) (Codec[S, R], error) 183 | } 184 | 185 | */ 186 | 187 | // Protocol codec factory object for RpcDataPackage 188 | type RpcDataPackageProtocol[S, R *RpcDataPackage] struct { 189 | timeout *time.Duration 190 | 191 | tw *timewheel.TimeWheel[int64] 192 | 193 | chunkSize uint32 194 | } 195 | 196 | // NewRpcDataPackageProtocol create a RpcDataPackageProtocol and start timewheel 197 | func NewRpcDataPackageProtocol[S, R *RpcDataPackage]() (*RpcDataPackageProtocol[S, R], error) { 198 | protocol := &RpcDataPackageProtocol[S, R]{} 199 | tw, err := timewheel.New[int64](chunkExpireTimewheelInterval, uint16(chunkExpireTimeWheelSlot)) 200 | if err != nil { 201 | return nil, err 202 | } 203 | protocol.tw = tw 204 | protocol.tw.Start() 205 | return protocol, nil 206 | } 207 | 208 | func (r *RpcDataPackageProtocol[S, R]) NewCodec(rw io.ReadWriter) (link.Codec[*RpcDataPackage, *RpcDataPackage], error) { 209 | rpcDataPackage := &RpcDataPackageCodec[S, R]{ 210 | readWriter: rw, 211 | p: r, 212 | timeout: r.timeout, 213 | chunkPackageCache: make(map[int64]*RpcDataPackage), 214 | } 215 | 216 | rpcDataPackage.closer, _ = rw.(io.Closer) 217 | 218 | return rpcDataPackage, nil 219 | 220 | } 221 | 222 | // Stop 223 | func (r *RpcDataPackageProtocol[S, R]) Stop() { 224 | if r.tw != nil { 225 | r.tw.Stop() 226 | } 227 | } 228 | 229 | // Here end to implements link module Codec interface 230 | -------------------------------------------------------------------------------- /docs/Demo.md: -------------------------------------------------------------------------------- 1 |

baidurpc

2 | 3 |

4 | baidurpc是一种基于TCP协议的二进制高性能RPC通信协议实现。它以Protobuf作为基本的数据交换格式。 5 | 本版本基于golang实现.完全兼容jprotobuf-rpc-socket: https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 6 |

7 | 8 | ### 更多特性使用介绍 9 | 10 | #### 开发RPC服务端 11 | 12 | 1. 定义PB对象 13 | ```property 14 | message DataMessae { 15 | string name = 1; 16 | } 17 | ``` 18 | 用protoc工具 生成 pb go 定义文件 19 | protoc --go_out=. datamessage.proto 20 | 2. 定义一个对象以及方法,用于发布服务 21 | 22 | ```go 23 | 24 | type EchoService struct { 25 | } 26 | 27 | // Echo test publish method with return type has context argument 28 | // 方法要求 29 | // 参数个数必须为1个或2个, 第一个类型必须为 context.Context 30 | // 第二个类型必须是实现 proto.Message接口(如果是无参,可以省略) 31 | // 返回个数可以为1个或2个 第一个类型必须是实现 proto.Message接口 32 | // 第2个参数为可选。 当使用时,必须为 context.Context类型 33 | func (rpc *EchoService) Echo(c context.Context, in *DataMessage) (*DataMessage, context.Context) { 34 | var ret = "hello " 35 | 36 | // if receive with attachement 37 | attachement := baidurpc.Attachement(c) 38 | fmt.Println(c) 39 | 40 | if len(*in.Name) == 0 { 41 | ret = ret + "veryone" 42 | } else { 43 | ret = ret + *in.Name 44 | } 45 | dm := DataMessage{} 46 | dm.Name = proto.String(ret) 47 | return &dm, baidurpc.BindAttachement(context.Background(), []byte("hello")) // return with attachement 48 | } 49 | ``` 50 | 51 | 以下都是合法的定义方法 52 | 1. Echo(c context.Context, in *DataMessage) (*DataMessage, context.Context) 53 | 2. Echo(c context.Context) (*DataMessage, context.Context) 54 | 3. Echo(c context.Context) (*DataMessage) 55 | 56 | 57 | 2. 指定发布端口,把EchoService发布成RPC服务 58 | 59 | ```go 60 | serverMeta := baidurpc.ServerMeta{} 61 | serverMeta.Host = nil 62 | serverMeta.Port = Int(*port) 63 | // set chunk size this will open server chunk package by specified size 64 | // serverMeta.ChunkSize = 1024 // 1k 65 | rpcServer := baidurpc.NewTpcServer(&serverMeta) 66 | 67 | echoService := new(EchoService) 68 | 69 | // mapping可选,如果需要映射成新的function名称时使用 70 | mapping := make(map[string]string) 71 | mapping["Echo"] = "echo" 72 | // 第一个参数 "echoService" 为空时,则会使用 EchoService的struct 的type name 73 | rpcServer.RegisterNameWithMethodMapping("echoService", echoService, mapping) 74 | // 最简注册方式 rpcServer.Register(echoService) 75 | 76 | // 启动RPC服务 77 | err := rpcServer.StartAndBlock() 78 | 79 | if err != nil { 80 | baidurpc.Error(err) 81 | os.Exit(-1) 82 | } 83 | ``` 84 | 85 | 至此RPC已经开发完成,运行上面代码,就可以发布完成. 86 | 87 | #### 开发启验证功能 88 | 89 | 实现 AuthService 接口 90 | 91 | ```go 92 | type StringMatchAuthService struct { 93 | } 94 | 95 | // Authenticate 96 | func (as *StringMatchAuthService) Authenticate(service, name string, authToken []byte) bool { 97 | if authToken == nil { 98 | return false 99 | } 100 | return strings.Compare(AUTH_TOKEN, string(authToken)) == 0 101 | } 102 | 103 | ``` 104 | 设置到service对象 105 | ```go 106 | 107 | // ... 108 | rpcServer := baidurpc.NewTpcServer(&serverMeta) 109 | rpcServer.SetAuthService(new(StringMatchAuthService)) 110 | 111 | ``` 112 | 113 | #### 设置trace功能 114 | 115 | 实现 TraceService 接口 116 | 117 | ```go 118 | type AddOneTraceService struct { 119 | } 120 | 121 | // Trace 122 | func (as *AddOneTraceService) Trace(service, name string, traceInfo *baidurpc.TraceInfo) *baidurpc.TraceInfo { 123 | *traceInfo.SpanId++ 124 | *traceInfo.TraceId++ 125 | *traceInfo.ParentSpanId++ 126 | return traceInfo 127 | } 128 | 129 | ``` 130 | 131 | 设置到service对象 132 | ```go 133 | 134 | // ... 135 | rpcServer := baidurpc.NewTpcServer(&serverMeta) 136 | rpcServer.SetTraceService(new(AddOneTraceService)) 137 | 138 | ``` 139 | 140 | ### 开发RPC客户端 141 | 142 | ```go 143 | // 创建链接(本示例使用连接池方式) 144 | url := baidurpc.URL{} 145 | url.SetHost(host).SetPort(port) 146 | timeout := time.Second * 5 147 | 148 | // 创建连接 149 | connection, err := baidurpc.NewDefaultTCPConnectionPool(url, &timeout) 150 | if err != nil { 151 | fmt.Println(err) 152 | os.Exit(-1) 153 | } 154 | defer connection.Close() 155 | 156 | // 创建client 157 | rpcClient, err := baidurpc.NewRpcCient(connection) 158 | if err != nil { 159 | fmt.Println(err) 160 | os.Exit(-1) 161 | } 162 | defer rpcClient.Close() 163 | // 调用RPC 164 | serviceName := "echoService" 165 | methodName := "echo" 166 | rpcInvocation := baidurpc.NewRpcInvocation(&serviceName, &methodName) 167 | 168 | message := "say hello from xiemalin中文测试" 169 | dm := DataMessage{&message} 170 | 171 | rpcInvocation.SetParameterIn(&dm) 172 | rpcInvocation.LogId = proto.Int64(1) 173 | 174 | // 可选, 设置logid 与 附件 175 | // rpcInvocation.LogId = proto.Int64(1) 176 | // rpcInvocation.Attachment = []byte("this is attachement contenet") 177 | 178 | parameterOut := DataMessage{} 179 | 180 | response, err := rpcClient.SendRpcRequest(rpcInvocation, ¶meterOut) 181 | if err != nil { 182 | fmt.Println(err) 183 | os.Exit(-1) 184 | } 185 | 186 | if response == nil { 187 | fmt.Println("Reponse is nil") 188 | return 189 | } 190 | ``` 191 | 192 | ### 设置调用超时 193 | 194 | ```go 195 | // baidurpc的超时控制功能使用了 时间轮 timewheel功能 https://github.com/jhunters/timewheel 196 | // 可以在初始化Client时设置 197 | timewheelInterval := 1 * time.Second 198 | var timewheelSlot uint16 = 300 199 | rpcClient, err := baidurpc.NewRpcCientWithTimeWheelSetting(connection, timewheelInterval, timewheelSlot) 200 | 201 | // 调用时,设置超时功能 202 | response, err := rpcClient.SendRpcRequestWithTimeout(100*time.Millisecond, rpcInvocation, ¶meterOut) 203 | // 如果发生超时, 返回的错误码为 62 204 | 205 | ``` 206 | 207 | ### 设置验证 208 | ```go 209 | // 调用RPC 210 | serviceName := "echoService" 211 | methodName := "echo" 212 | rpcInvocation := baidurpc.NewRpcInvocation(&serviceName, &methodName) 213 | // set auth token 214 | rpcInvocation.AuthenticateData = []byte("AUTH_TOKEN") 215 | // 调用时,设置超时功能 216 | response, err := rpcClient.SendRpcRequestWithTimeout(100*time.Millisecond, rpcInvocation, ¶meterOut) 217 | // 如果发生超时, 返回的错误码为 62 218 | 219 | ``` 220 | 221 | ### 设置分包chunk功能 222 | ```go 223 | // 调用RPC 224 | serviceName := "echoService" 225 | methodName := "echo" 226 | rpcInvocation := baidurpc.NewRpcInvocation(&serviceName, &methodName) 227 | // 设置分包大小(byte) 228 | rpcInvocation.ChunkSize = 1024 //1k 229 | // 调用时,设置超时功能 230 | response, err := rpcClient.SendRpcRequestWithTimeout(100*time.Millisecond, rpcInvocation, ¶meterOut) 231 | // 如果发生超时, 返回的错误码为 62 232 | 233 | ``` 234 | 235 | ### 设置Trace功能 236 | ```go 237 | // 调用RPC 238 | serviceName := "echoService" 239 | methodName := "echo" 240 | rpcInvocation := baidurpc.NewRpcInvocation(&serviceName, &methodName) 241 | // 设置trace信息 242 | rpcInvocation.TraceId = 10 243 | rpcInvocation.SpanId = 11 244 | rpcInvocation.ParentSpanId = 12 245 | rpcInvocation.RpcRequestMetaExt = map[string]string{"key1": "value1"} 246 | // 调用时,设置超时功能 247 | response, err := rpcClient.SendRpcRequestWithTimeout(100*time.Millisecond, rpcInvocation, ¶meterOut) 248 | // 如果发生超时, 返回的错误码为 62 249 | 250 | // 获取服务端返回的trace信息 251 | response.GetTraceId() 252 | response.GetParentSpanId() 253 | response.GetParentSpanId() 254 | response.GetRpcRequestMetaExt() 255 | 256 | ``` 257 | 258 | 259 | ### 开发Ha RPC客户端 260 | 261 | ```go 262 | urls := []baidurpc.URL{{Host: host, Port: &errPort}, {Host: host, Port: port}} 263 | 264 | connections, err := baidurpc.NewBatchTCPConnection(urls, timeout) 265 | if err != nil { 266 | fmt.Println(err) 267 | os.Exit(-1) 268 | } 269 | defer baidurpc.CloseBatchConnection(connections) 270 | 271 | haClient, err := baidurpc.NewHaRpcCient(connections) 272 | if err != nil { 273 | fmt.Println(err) 274 | os.Exit(-1) 275 | } 276 | 277 | serviceName := "echoService" 278 | methodName := "echo" 279 | rpcInvocation := baidurpc.NewRpcInvocation(&serviceName, &methodName) 280 | 281 | message := "say hello from xiemalin中文测试" 282 | dm := DataMessage{&message} 283 | 284 | rpcInvocation.SetParameterIn(&dm) 285 | rpcInvocation.LogId = proto.Int64(1) 286 | rpcInvocation.Attachment = []byte("hello world") 287 | 288 | parameterOut := DataMessage{} 289 | 290 | response, err := haClient.SendRpcRequest(rpcInvocation, ¶meterOut) 291 | if err != nil { 292 | fmt.Println(err) 293 | os.Exit(-1) 294 | } 295 | 296 | if response == nil { 297 | fmt.Println("Reponse is nil") 298 | return 299 | } 300 | 301 | fmt.Println("attachement", response.Attachment) 302 | 303 | ``` 304 | -------------------------------------------------------------------------------- /pb_status.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go v1.26.0 4 | // protoc v3.9.2 5 | // source: go.proto 6 | 7 | package baidurpc 8 | 9 | import ( 10 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 11 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 12 | reflect "reflect" 13 | sync "sync" 14 | ) 15 | 16 | const ( 17 | // Verify that this generated code is sufficiently up-to-date. 18 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 19 | // Verify that runtime/protoimpl is sufficiently up-to-date. 20 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 21 | ) 22 | 23 | type RPCStatus struct { 24 | state protoimpl.MessageState 25 | sizeCache protoimpl.SizeCache 26 | unknownFields protoimpl.UnknownFields 27 | 28 | Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` 29 | Port int32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` 30 | TimeoutSenconds int32 `protobuf:"varint,3,opt,name=timeout,proto3" json:"timeout,omitempty"` 31 | Methods []*RPCMethod `protobuf:"bytes,4,rep,name=methods,proto3" json:"methods,omitempty"` 32 | } 33 | 34 | func (x *RPCStatus) Reset() { 35 | *x = RPCStatus{} 36 | if protoimpl.UnsafeEnabled { 37 | mi := &file_go_proto_msgTypes[0] 38 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 39 | ms.StoreMessageInfo(mi) 40 | } 41 | } 42 | 43 | func (x *RPCStatus) String() string { 44 | return protoimpl.X.MessageStringOf(x) 45 | } 46 | 47 | func (*RPCStatus) ProtoMessage() {} 48 | 49 | func (x *RPCStatus) ProtoReflect() protoreflect.Message { 50 | mi := &file_go_proto_msgTypes[0] 51 | if protoimpl.UnsafeEnabled && x != nil { 52 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 53 | if ms.LoadMessageInfo() == nil { 54 | ms.StoreMessageInfo(mi) 55 | } 56 | return ms 57 | } 58 | return mi.MessageOf(x) 59 | } 60 | 61 | // Deprecated: Use RPCStatus.ProtoReflect.Descriptor instead. 62 | func (*RPCStatus) Descriptor() ([]byte, []int) { 63 | return file_go_proto_rawDescGZIP(), []int{0} 64 | } 65 | 66 | func (x *RPCStatus) GetHost() string { 67 | if x != nil { 68 | return x.Host 69 | } 70 | return "" 71 | } 72 | 73 | func (x *RPCStatus) GetPort() int32 { 74 | if x != nil { 75 | return x.Port 76 | } 77 | return 0 78 | } 79 | 80 | func (x *RPCStatus) GetTimeout() int32 { 81 | if x != nil { 82 | return x.TimeoutSenconds 83 | } 84 | return 0 85 | } 86 | 87 | func (x *RPCStatus) GetMethods() []*RPCMethod { 88 | if x != nil { 89 | return x.Methods 90 | } 91 | return nil 92 | } 93 | 94 | type RPCMethod struct { 95 | state protoimpl.MessageState 96 | sizeCache protoimpl.SizeCache 97 | unknownFields protoimpl.UnknownFields 98 | 99 | Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` 100 | Method string `protobuf:"bytes,2,opt,name=method,proto3" json:"method,omitempty"` 101 | InTypeMeta string `protobuf:"bytes,3,opt,name=intype,proto3" json:"intype,omitempty"` 102 | ReturnTypeMeta string `protobuf:"bytes,4,opt,name=returntype,proto3" json:"returntype,omitempty"` 103 | } 104 | 105 | func (x *RPCMethod) Reset() { 106 | *x = RPCMethod{} 107 | if protoimpl.UnsafeEnabled { 108 | mi := &file_go_proto_msgTypes[1] 109 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 110 | ms.StoreMessageInfo(mi) 111 | } 112 | } 113 | 114 | func (x *RPCMethod) String() string { 115 | return protoimpl.X.MessageStringOf(x) 116 | } 117 | 118 | func (*RPCMethod) ProtoMessage() {} 119 | 120 | func (x *RPCMethod) ProtoReflect() protoreflect.Message { 121 | mi := &file_go_proto_msgTypes[1] 122 | if protoimpl.UnsafeEnabled && x != nil { 123 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 124 | if ms.LoadMessageInfo() == nil { 125 | ms.StoreMessageInfo(mi) 126 | } 127 | return ms 128 | } 129 | return mi.MessageOf(x) 130 | } 131 | 132 | // Deprecated: Use RPCMethod.ProtoReflect.Descriptor instead. 133 | func (*RPCMethod) Descriptor() ([]byte, []int) { 134 | return file_go_proto_rawDescGZIP(), []int{1} 135 | } 136 | 137 | func (x *RPCMethod) GetService() string { 138 | if x != nil { 139 | return x.Service 140 | } 141 | return "" 142 | } 143 | 144 | func (x *RPCMethod) GetMethod() string { 145 | if x != nil { 146 | return x.Method 147 | } 148 | return "" 149 | } 150 | 151 | func (x *RPCMethod) GetInTypeMeta() string { 152 | if x != nil { 153 | return x.InTypeMeta 154 | } 155 | return "" 156 | } 157 | 158 | func (x *RPCMethod) GetReturnTypeMeta() string { 159 | if x != nil { 160 | return x.ReturnTypeMeta 161 | } 162 | return "" 163 | } 164 | 165 | type QpsData struct { 166 | state protoimpl.MessageState 167 | sizeCache protoimpl.SizeCache 168 | unknownFields protoimpl.UnknownFields 169 | 170 | Qpsinfo map[int64]int32 `protobuf:"bytes,1,rep,name=qpsinfo,proto3" json:"qpsinfo,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` 171 | } 172 | 173 | func (x *QpsData) Reset() { 174 | *x = QpsData{} 175 | if protoimpl.UnsafeEnabled { 176 | mi := &file_go_proto_msgTypes[2] 177 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 178 | ms.StoreMessageInfo(mi) 179 | } 180 | } 181 | 182 | func (x *QpsData) String() string { 183 | return protoimpl.X.MessageStringOf(x) 184 | } 185 | 186 | func (*QpsData) ProtoMessage() {} 187 | 188 | func (x *QpsData) ProtoReflect() protoreflect.Message { 189 | mi := &file_go_proto_msgTypes[2] 190 | if protoimpl.UnsafeEnabled && x != nil { 191 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 192 | if ms.LoadMessageInfo() == nil { 193 | ms.StoreMessageInfo(mi) 194 | } 195 | return ms 196 | } 197 | return mi.MessageOf(x) 198 | } 199 | 200 | // Deprecated: Use QpsData.ProtoReflect.Descriptor instead. 201 | func (*QpsData) Descriptor() ([]byte, []int) { 202 | return file_go_proto_rawDescGZIP(), []int{2} 203 | } 204 | 205 | func (x *QpsData) GetQpsinfo() map[int64]int32 { 206 | if x != nil { 207 | return x.Qpsinfo 208 | } 209 | return nil 210 | } 211 | 212 | var File_go_proto protoreflect.FileDescriptor 213 | 214 | var file_go_proto_rawDesc = []byte{ 215 | 0x0a, 0x08, 0x67, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x73, 0x0a, 0x09, 0x52, 0x50, 216 | 0x43, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 217 | 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 218 | 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 219 | 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 220 | 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x24, 0x0a, 0x07, 0x6d, 0x65, 0x74, 221 | 0x68, 0x6f, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x52, 0x50, 0x43, 222 | 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x22, 223 | 0x85, 0x01, 0x0a, 0x09, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x18, 0x0a, 224 | 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 225 | 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 226 | 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 227 | 0x1e, 0x0a, 0x0a, 0x49, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x18, 0x03, 0x20, 228 | 0x01, 0x28, 0x09, 0x52, 0x0a, 0x49, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 229 | 0x26, 0x0a, 0x0e, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x4d, 0x65, 0x74, 230 | 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x54, 231 | 0x79, 0x70, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x22, 0x76, 0x0a, 0x07, 0x51, 0x70, 0x73, 0x44, 0x61, 232 | 0x74, 0x61, 0x12, 0x2f, 0x0a, 0x07, 0x71, 0x70, 0x73, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 233 | 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x51, 0x70, 0x73, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x51, 0x70, 234 | 0x73, 0x69, 0x6e, 0x66, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x71, 0x70, 0x73, 0x69, 235 | 0x6e, 0x66, 0x6f, 0x1a, 0x3a, 0x0a, 0x0c, 0x51, 0x70, 0x73, 0x69, 0x6e, 0x66, 0x6f, 0x45, 0x6e, 236 | 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 237 | 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 238 | 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 239 | 0x22, 0x5a, 0x20, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x61, 240 | 0x69, 0x64, 0x75, 0x2d, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x62, 0x61, 0x69, 0x64, 0x75, 241 | 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 242 | } 243 | 244 | var ( 245 | file_go_proto_rawDescOnce sync.Once 246 | file_go_proto_rawDescData = file_go_proto_rawDesc 247 | ) 248 | 249 | func file_go_proto_rawDescGZIP() []byte { 250 | file_go_proto_rawDescOnce.Do(func() { 251 | file_go_proto_rawDescData = protoimpl.X.CompressGZIP(file_go_proto_rawDescData) 252 | }) 253 | return file_go_proto_rawDescData 254 | } 255 | 256 | var file_go_proto_msgTypes = make([]protoimpl.MessageInfo, 4) 257 | var file_go_proto_goTypes = []interface{}{ 258 | (*RPCStatus)(nil), // 0: RPCStatus 259 | (*RPCMethod)(nil), // 1: RPCMethod 260 | (*QpsData)(nil), // 2: QpsData 261 | nil, // 3: QpsData.QpsinfoEntry 262 | } 263 | var file_go_proto_depIdxs = []int32{ 264 | 1, // 0: RPCStatus.methods:type_name -> RPCMethod 265 | 3, // 1: QpsData.qpsinfo:type_name -> QpsData.QpsinfoEntry 266 | 2, // [2:2] is the sub-list for method output_type 267 | 2, // [2:2] is the sub-list for method input_type 268 | 2, // [2:2] is the sub-list for extension type_name 269 | 2, // [2:2] is the sub-list for extension extendee 270 | 0, // [0:2] is the sub-list for field type_name 271 | } 272 | 273 | func init() { file_go_proto_init() } 274 | func file_go_proto_init() { 275 | if File_go_proto != nil { 276 | return 277 | } 278 | if !protoimpl.UnsafeEnabled { 279 | file_go_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 280 | switch v := v.(*RPCStatus); i { 281 | case 0: 282 | return &v.state 283 | case 1: 284 | return &v.sizeCache 285 | case 2: 286 | return &v.unknownFields 287 | default: 288 | return nil 289 | } 290 | } 291 | file_go_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { 292 | switch v := v.(*RPCMethod); i { 293 | case 0: 294 | return &v.state 295 | case 1: 296 | return &v.sizeCache 297 | case 2: 298 | return &v.unknownFields 299 | default: 300 | return nil 301 | } 302 | } 303 | file_go_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { 304 | switch v := v.(*QpsData); i { 305 | case 0: 306 | return &v.state 307 | case 1: 308 | return &v.sizeCache 309 | case 2: 310 | return &v.unknownFields 311 | default: 312 | return nil 313 | } 314 | } 315 | } 316 | type x struct{} 317 | out := protoimpl.TypeBuilder{ 318 | File: protoimpl.DescBuilder{ 319 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 320 | RawDescriptor: file_go_proto_rawDesc, 321 | NumEnums: 0, 322 | NumMessages: 4, 323 | NumExtensions: 0, 324 | NumServices: 0, 325 | }, 326 | GoTypes: file_go_proto_goTypes, 327 | DependencyIndexes: file_go_proto_depIdxs, 328 | MessageInfos: file_go_proto_msgTypes, 329 | }.Build() 330 | File_go_proto = out.File 331 | file_go_proto_rawDesc = nil 332 | file_go_proto_goTypes = nil 333 | file_go_proto_depIdxs = nil 334 | } 335 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /client.go: -------------------------------------------------------------------------------- 1 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 2 | // 3 | // Copyright 2002-2007 the original author or authors. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | package baidurpc 17 | 18 | import ( 19 | "errors" 20 | "fmt" 21 | "log" 22 | "net" 23 | "sync/atomic" 24 | "time" 25 | 26 | "github.com/jhunters/goassist/concurrent/syncx" 27 | "github.com/jhunters/timewheel" 28 | "google.golang.org/protobuf/proto" 29 | ) 30 | 31 | var ( 32 | defaultTimewheelInterval = 10 * time.Millisecond 33 | defaultTimewheelSlot = 300 34 | 35 | errNeedInit = errors.New("[client-001]Session is not initialized, Please use NewRpcInvocation() to create instance") 36 | errResponseNil = errors.New("[client-003]No response result, mybe net work broken error") 37 | LOG_SERVER_RESPONSE_ERROR = "[client-002]Server response error. code=%d, msg='%s'" 38 | LOG_CLIENT_TIMECOUST_INFO = "[client-101]Server name '%s' method '%s' process cost '%.5g' seconds" 39 | 40 | closedTimeOut = time.Duration(0) 41 | ) 42 | 43 | const ( 44 | ST_READ_TIMEOUT = 62 45 | ) 46 | 47 | /* 48 | RPC client invoke 49 | */ 50 | type RpcClient struct { 51 | Session Connection 52 | tw *timewheel.TimeWheel[*RpcDataPackage] 53 | 54 | // 单次请求唯一标识 55 | correlationId int64 56 | // async request state map 57 | requestCallState *syncx.Map[int64, chan *RpcDataPackage] // use sync map for cocurrent access 58 | 59 | // to close loop receive 60 | closeChan chan bool 61 | 62 | asyncMode bool 63 | } 64 | 65 | // URL with host and port attribute 66 | type URL struct { 67 | Host *string 68 | Port *int 69 | } 70 | 71 | // SetHost set host name 72 | func (u *URL) SetHost(host *string) *URL { 73 | u.Host = host 74 | return u 75 | } 76 | 77 | // SetPort set port 78 | func (u *URL) SetPort(port *int) *URL { 79 | u.Port = port 80 | return u 81 | } 82 | 83 | // RpcInvocation define rpc invocation 84 | type RpcInvocation struct { 85 | ServiceName *string 86 | MethodName *string 87 | ParameterIn *proto.Message 88 | Attachment []byte 89 | LogId *int64 90 | CompressType *int32 91 | AuthenticateData []byte 92 | ChunkSize uint32 93 | TraceId int64 94 | SpanId int64 95 | ParentSpanId int64 96 | RpcRequestMetaExt map[string]string 97 | } 98 | 99 | // NewRpcCient new rpc client 100 | func NewRpcCient(connection Connection) (*RpcClient, error) { 101 | return NewRpcCientWithTimeWheelSetting(connection, defaultTimewheelInterval, uint16(defaultTimewheelSlot)) 102 | } 103 | 104 | // NewRpcCientWithTimeWheelSetting new rpc client with set timewheel settings 105 | func NewRpcCientWithTimeWheelSetting(connection Connection, timewheelInterval time.Duration, timewheelSlot uint16) (*RpcClient, error) { 106 | c := RpcClient{} 107 | c.Session = connection 108 | 109 | // async mode not support under pooled connection 110 | _, pooled := connection.(*TCPConnectionPool) 111 | c.asyncMode = !pooled 112 | 113 | // initial timewheel to process async request on time out event handle 114 | c.tw, _ = timewheel.New[*RpcDataPackage](timewheelInterval, timewheelSlot) 115 | c.tw.Start() 116 | c.closeChan = make(chan bool, 1) 117 | c.requestCallState = syncx.NewMap[int64, chan *RpcDataPackage]() // make(map[int64]chan *RpcDataPackage) 118 | 119 | if c.asyncMode { // only enabled on async mode 120 | go c.startLoopReceive() 121 | } 122 | return &c, nil 123 | } 124 | 125 | // NewRpcInvocation create RpcInvocation with service name and method name 126 | func NewRpcInvocation(serviceName, methodName *string) *RpcInvocation { 127 | r := new(RpcInvocation) 128 | r.init(serviceName, methodName) 129 | 130 | return r 131 | } 132 | 133 | func (r *RpcInvocation) init(serviceName, methodName *string) { 134 | 135 | *r = RpcInvocation{} 136 | r.ServiceName = serviceName 137 | r.MethodName = methodName 138 | compressType := COMPRESS_NO 139 | r.CompressType = &compressType 140 | r.ParameterIn = nil 141 | } 142 | 143 | // SetParameterIn 144 | func (r *RpcInvocation) SetParameterIn(parameterIn proto.Message) { 145 | r.ParameterIn = ¶meterIn 146 | } 147 | 148 | // GetRequestRpcDataPackage 149 | func (r *RpcInvocation) GetRequestRpcDataPackage() (*RpcDataPackage, error) { 150 | 151 | rpcDataPackage := new(RpcDataPackage) 152 | rpcDataPackage.ServiceName(*r.ServiceName) 153 | rpcDataPackage.MethodName(*r.MethodName) 154 | rpcDataPackage.MagicCode(MAGIC_CODE) 155 | rpcDataPackage.AuthenticationData(r.AuthenticateData) 156 | rpcDataPackage.chunkSize = r.ChunkSize 157 | rpcDataPackage.TraceId(r.TraceId) 158 | rpcDataPackage.SpanId(r.SpanId) 159 | rpcDataPackage.ParentSpanId(r.ParentSpanId) 160 | rpcDataPackage.RpcRequestMetaExt(r.RpcRequestMetaExt) 161 | if r.CompressType != nil { 162 | rpcDataPackage.CompressType(*r.CompressType) 163 | } 164 | if r.LogId != nil { 165 | rpcDataPackage.LogId(*r.LogId) 166 | } 167 | 168 | rpcDataPackage.SetAttachment(r.Attachment) 169 | 170 | if r.ParameterIn != nil { 171 | data, err := proto.Marshal(*r.ParameterIn) 172 | if err != nil { 173 | return nil, err 174 | } 175 | rpcDataPackage.SetData(data) 176 | } 177 | 178 | return rpcDataPackage, nil 179 | } 180 | 181 | // define client methods 182 | // Close close client with time wheel 183 | func (c *RpcClient) Close() { 184 | c.closeChan <- true 185 | if c.tw != nil { 186 | c.tw.Stop() 187 | } 188 | } 189 | 190 | func (c *RpcClient) startLoopReceive() { 191 | for { 192 | 193 | select { 194 | case <-c.closeChan: 195 | // exit loop 196 | return 197 | default: 198 | dataPackage, err := c.safeReceive() 199 | if err != nil { 200 | 201 | netErr, ok := err.(*net.OpError) 202 | if ok { 203 | // if met network error, wait some time to retry or call client close method to close loop if met net error 204 | // error maybe about broken network or closed network 205 | log.Println(netErr) 206 | if !netErr.Timeout() { 207 | // try reconnect 208 | c.Session.Reconnect() 209 | } 210 | 211 | } 212 | time.Sleep(200 * time.Millisecond) 213 | 214 | } 215 | 216 | if dataPackage != nil && dataPackage.Meta != nil { 217 | correlationId := dataPackage.Meta.GetCorrelationId() 218 | v, exist := c.requestCallState.LoadAndDelete(correlationId) // [correlationId] 219 | if !exist { 220 | // bad response correlationId 221 | Errorf("bad correlationId '%d' not exist ", correlationId) 222 | continue 223 | } 224 | go func() { 225 | v <- dataPackage 226 | }() 227 | } 228 | } 229 | 230 | } 231 | } 232 | 233 | func (c *RpcClient) safeReceive() (*RpcDataPackage, error) { 234 | defer func() { 235 | if p := recover(); p != nil { 236 | Warningf("receive catched panic error %v", p) 237 | } 238 | }() 239 | return c.Session.Receive() 240 | } 241 | 242 | // asyncRequest 243 | func (c *RpcClient) asyncRequest(timeout time.Duration, request *RpcDataPackage, ch chan *RpcDataPackage) { 244 | // create a task bind with key, data and time out call back function. 245 | t := &timewheel.Task[*RpcDataPackage]{ 246 | Data: request, // business data 247 | TimeoutCallback: func(task timewheel.Task[*RpcDataPackage]) { // call back function on time out 248 | // process someting after time out happened. 249 | errorcode := int32(ST_READ_TIMEOUT) 250 | task.Data.ErrorCode(errorcode) 251 | errormsg := fmt.Sprintf("request time out of %v", task.Delay()) 252 | task.Data.ErrorText(errormsg) 253 | ch <- request 254 | }} 255 | 256 | // add task and return unique task id 257 | taskid, err := c.tw.AddTask(timeout, *t) // add delay task 258 | if err != nil { 259 | errorcode := int32(ST_ERROR) 260 | request.ErrorCode(errorcode) 261 | errormsg := err.Error() 262 | request.ErrorText(errormsg) 263 | 264 | ch <- request 265 | return 266 | } 267 | 268 | defer func() { 269 | c.tw.RemoveTask(taskid) 270 | if e := recover(); e != nil { 271 | Warningf("asyncRequest failed with error %v", e) 272 | } 273 | }() 274 | 275 | rsp, err := c.doSendReceive(request, ch) 276 | if err != nil { 277 | errorcode := int32(ST_ERROR) 278 | request.ErrorCode(errorcode) 279 | errormsg := err.Error() 280 | request.ErrorText(errormsg) 281 | 282 | ch <- request 283 | return 284 | } 285 | 286 | ch <- rsp 287 | } 288 | 289 | func (c *RpcClient) doSendReceive(rpcDataPackage *RpcDataPackage, ch <-chan *RpcDataPackage) (*RpcDataPackage, error) { 290 | if c.asyncMode { 291 | err := c.Session.Send(rpcDataPackage) 292 | if err != nil { 293 | return nil, err 294 | } 295 | // async wait response 296 | return <-ch, nil 297 | } 298 | // not async mode use block request 299 | return c.Session.SendReceive(rpcDataPackage) 300 | 301 | } 302 | 303 | // SendRpcRequest send rpc request to remote server 304 | func (c *RpcClient) SendRpcRequest(rpcInvocation *RpcInvocation, responseMessage proto.Message) (*RpcDataPackage, error) { 305 | return c.SendRpcRequestWithTimeout(closedTimeOut, rpcInvocation, responseMessage) 306 | 307 | } 308 | 309 | // SendRpcRequest send rpc request to remote server 310 | func (c *RpcClient) SendRpcRequestWithTimeout(timeout time.Duration, rpcInvocation *RpcInvocation, responseMessage proto.Message) (*RpcDataPackage, error) { 311 | if c.Session == nil { 312 | return nil, errNeedInit 313 | } 314 | 315 | now := time.Now().UnixNano() 316 | 317 | rpcDataPackage, err := rpcInvocation.GetRequestRpcDataPackage() 318 | if err != nil { 319 | return nil, err 320 | } 321 | 322 | // set request unique id 323 | correlationId := atomic.AddInt64(&c.correlationId, 1) 324 | rpcDataPackage.CorrelationId(correlationId) 325 | 326 | var rsp *RpcDataPackage 327 | if c.asyncMode { 328 | ch := make(chan *RpcDataPackage, 1) 329 | c.requestCallState.Store(correlationId, ch) 330 | 331 | if timeout > 0 { 332 | go c.asyncRequest(timeout, rpcDataPackage, ch) 333 | rsp = <-ch //异步等待返回, 可能返回的情况 1. 本地错误或超时返回 2. startLoopReceive 监听远程数据返回 334 | } else { 335 | rsp, err = c.doSendReceive(rpcDataPackage, ch) 336 | } 337 | 338 | } else { 339 | if timeout > 0 { 340 | ch := make(chan *RpcDataPackage, 1) 341 | go c.asyncRequest(timeout, rpcDataPackage, ch) 342 | defer close(ch) 343 | // wait for message 344 | rsp = <-ch 345 | } else { 346 | rsp, err = c.Session.SendReceive(rpcDataPackage) 347 | } 348 | } 349 | 350 | if err != nil { 351 | errorcode := int32(ST_ERROR) 352 | rpcDataPackage.ErrorCode(errorcode) 353 | errormsg := err.Error() 354 | rpcDataPackage.ErrorText(errormsg) 355 | return rpcDataPackage, err 356 | } 357 | 358 | r := rsp 359 | if r == nil { 360 | return nil, errResponseNil //to ignore this nil value 361 | } 362 | 363 | errorCode := r.GetMeta().GetResponse().GetErrorCode() 364 | if errorCode > 0 { 365 | errMsg := fmt.Sprintf(LOG_SERVER_RESPONSE_ERROR, 366 | errorCode, r.GetMeta().GetResponse().GetErrorText()) 367 | return r, errors.New(errMsg) 368 | } 369 | 370 | response := r.GetData() 371 | if response != nil { 372 | err = proto.Unmarshal(response, responseMessage) 373 | if err != nil { 374 | return r, err 375 | } 376 | } 377 | 378 | took := TimetookInSeconds(now) 379 | Infof(LOG_CLIENT_TIMECOUST_INFO, *rpcInvocation.ServiceName, *rpcInvocation.MethodName, took) 380 | 381 | return r, nil 382 | 383 | } 384 | 385 | // RpcResult Rpc response result from client request api under asynchronous way 386 | type RpcResult struct { 387 | rpcData *RpcDataPackage 388 | err error 389 | message proto.Message 390 | } 391 | 392 | func (rr *RpcResult) Get() proto.Message { 393 | return rr.message 394 | } 395 | 396 | func (rr *RpcResult) GetRpcDataPackage() *RpcDataPackage { 397 | return rr.rpcData 398 | } 399 | 400 | func (rr *RpcResult) GetErr() error { 401 | return rr.err 402 | } 403 | 404 | // SendRpcRequestAsyc send rpc request to remote server in asynchronous way 405 | func (c *RpcClient) SendRpcRequestAsyc(rpcInvocation *RpcInvocation, responseMessage proto.Message) <-chan *RpcResult { 406 | ch := make(chan *RpcResult, 1) 407 | 408 | go func() { 409 | defer func() { 410 | if p := recover(); p != nil { 411 | if err, ok := p.(error); ok { 412 | r := &RpcResult{nil, err, responseMessage} 413 | ch <- r 414 | } 415 | } 416 | }() 417 | 418 | resp, err := c.SendRpcRequest(rpcInvocation, responseMessage) 419 | result := &RpcResult{resp, err, responseMessage} 420 | ch <- result 421 | }() 422 | 423 | return ch 424 | } 425 | -------------------------------------------------------------------------------- /client_test.go: -------------------------------------------------------------------------------- 1 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 2 | // 3 | // Copyright 2002-2007 the original author or authors. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | package baidurpc_test 17 | 18 | import ( 19 | "strings" 20 | "testing" 21 | "time" 22 | 23 | baidurpc "github.com/baidu-golang/pbrpc" 24 | . "github.com/smartystreets/goconvey/convey" 25 | "google.golang.org/protobuf/proto" 26 | ) 27 | 28 | const ( 29 | AUTH_TOKEN = "SJIVNCQIN@#$@*sdjfsd" 30 | ) 31 | 32 | type ( 33 | StringMatchAuthService struct { 34 | } 35 | 36 | AddOneTraceService struct { 37 | } 38 | ) 39 | 40 | // Authenticate 41 | func (as *StringMatchAuthService) Authenticate(service, name string, authToken []byte) bool { 42 | if authToken == nil { 43 | return false 44 | } 45 | return strings.Compare(AUTH_TOKEN, string(authToken)) == 0 46 | } 47 | 48 | // Trace 49 | func (as *AddOneTraceService) Trace(service, name string, traceInfo *baidurpc.TraceInfo) *baidurpc.TraceInfo { 50 | traceInfo.SpanId++ 51 | traceInfo.TraceId++ 52 | traceInfo.ParentSpanId++ 53 | return traceInfo 54 | } 55 | 56 | // TestSingleTcpConnectionClient 57 | func TestSingleTcpConnectionClient(t *testing.T) { 58 | Convey("TestSingleTcpConnectionClient", t, func() { 59 | tcpServer := startRpcServer(0) 60 | defer stopRpcServer(tcpServer) 61 | 62 | conn, client, err := createClient() 63 | So(err, ShouldBeNil) 64 | So(conn, ShouldNotBeNil) 65 | So(client, ShouldNotBeNil) 66 | defer client.Close() 67 | defer conn.Close() 68 | 69 | testSendRpc("Client send rpc request", client, false, false, 0, false) 70 | testSendRpc("Client send rpc request(async)", client, true, false, 0, false) 71 | }) 72 | } 73 | 74 | // TestSingleTcpConnectionClientWithAuthenticate 75 | func TestSingleTcpConnectionClientWithAuthenticate(t *testing.T) { 76 | Convey("TestSingleTcpConnectionClientWithAuthenticate", t, func() { 77 | tcpServer := startRpcServer(0) 78 | tcpServer.SetAuthService(new(StringMatchAuthService)) 79 | defer stopRpcServer(tcpServer) 80 | 81 | conn, client, err := createClient() 82 | So(err, ShouldBeNil) 83 | So(conn, ShouldNotBeNil) 84 | So(client, ShouldNotBeNil) 85 | defer client.Close() 86 | defer conn.Close() 87 | 88 | testSendRpc("Client send rpc request", client, false, true, 0, false) 89 | testSendRpc("Client send rpc request(async)", client, true, true, 0, false) 90 | }) 91 | } 92 | 93 | // TestSingleTcpConnectionClientWithChunk 94 | func TestSingleTcpConnectionClientWithChunk(t *testing.T) { 95 | Convey("TestSingleTcpConnectionClientWithChunk", t, func() { 96 | tcpServer := startRpcServer(0) 97 | tcpServer.SetAuthService(new(StringMatchAuthService)) 98 | defer stopRpcServer(tcpServer) 99 | 100 | conn, client, err := createClient() 101 | So(err, ShouldBeNil) 102 | So(conn, ShouldNotBeNil) 103 | So(client, ShouldNotBeNil) 104 | defer client.Close() 105 | defer conn.Close() 106 | 107 | testSendRpc("Client send rpc request", client, false, true, 20, false) 108 | testSendRpc("Client send rpc request(async)", client, true, true, 20, false) 109 | }) 110 | } 111 | 112 | // TestSingleTcpConnectionClientAndServerWithChunk 113 | func TestSingleTcpConnectionClientAndServerWithChunk(t *testing.T) { 114 | Convey("TestSingleTcpConnectionClientAndServerWithChunk", t, func() { 115 | tcpServer := startRpcServer(20) 116 | tcpServer.SetAuthService(new(StringMatchAuthService)) 117 | defer stopRpcServer(tcpServer) 118 | 119 | conn, client, err := createClient() 120 | So(err, ShouldBeNil) 121 | So(conn, ShouldNotBeNil) 122 | So(client, ShouldNotBeNil) 123 | defer client.Close() 124 | defer conn.Close() 125 | 126 | testSendRpc("Client send rpc request", client, false, true, 20, false) 127 | testSendRpc("Client send rpc request(async)", client, true, true, 20, false) 128 | }) 129 | } 130 | 131 | // TestSingleTcpConnectionClientWithBadChunkCase 132 | func TestSingleTcpConnectionClientWithBadChunkCase(t *testing.T) { 133 | Convey("TestSingleTcpConnectionClientWithBadChunkCase", t, func() { 134 | tcpServer := startRpcServer(0) 135 | defer stopRpcServer(tcpServer) 136 | 137 | conn, client, err := createClient() 138 | So(err, ShouldBeNil) 139 | So(conn, ShouldNotBeNil) 140 | So(client, ShouldNotBeNil) 141 | defer client.Close() 142 | defer conn.Close() 143 | 144 | serviceName := "EchoService" 145 | methodName := "echo" 146 | rpcInvocation := baidurpc.NewRpcInvocation(&serviceName, &methodName) 147 | 148 | name := "(马林)(matthew)(XML)(jhunters)" 149 | dm := DataMessage{Name: name} 150 | 151 | rpcInvocation.SetParameterIn(&dm) 152 | rpcInvocation.LogId = proto.Int64(1) 153 | 154 | dataPackage, err := rpcInvocation.GetRequestRpcDataPackage() 155 | So(err, ShouldBeNil) 156 | 157 | dataPackage.ChuckInfo(10, 1) // bad chunk data package 158 | go func() { 159 | client.Session.SendReceive(dataPackage) // send bad chunk data package server will block unitl timeout 160 | }() 161 | time.Sleep(1 * time.Second) 162 | doSimpleRPCInvokeWithSignatureWithConvey(client, "EchoService", "echo", false, false, false, false, false, 0, false) 163 | }) 164 | } 165 | 166 | // TestPooledTcpConnectionClient 167 | func TestPooledTcpConnectionClient(t *testing.T) { 168 | Convey("TestPooledTcpConnectionClient", t, func() { 169 | tcpServer := startRpcServer(0) 170 | defer stopRpcServer(tcpServer) 171 | 172 | conn, client, err := createPooledConnectionClient() 173 | So(err, ShouldBeNil) 174 | So(conn, ShouldNotBeNil) 175 | So(client, ShouldNotBeNil) 176 | defer client.Close() 177 | defer conn.Close() 178 | 179 | testSendRpc("Client send rpc request", client, false, true, 0, false) 180 | testSendRpc("Client send rpc request(async)", client, true, true, 0, false) 181 | }) 182 | } 183 | 184 | // TestSingleTcpConnectionClientByAsync 185 | func TestSingleTcpConnectionClientByAsync(t *testing.T) { 186 | Convey("TestSingleTcpConnectionClientByAsync", t, func() { 187 | tcpServer := startRpcServer(0) 188 | tcpServer.SetAuthService(new(StringMatchAuthService)) 189 | defer stopRpcServer(tcpServer) 190 | 191 | conn, client, err := createClient() 192 | So(err, ShouldBeNil) 193 | So(conn, ShouldNotBeNil) 194 | So(client, ShouldNotBeNil) 195 | defer client.Close() 196 | defer conn.Close() 197 | 198 | testSendRpc("Client send rpc request", client, false, true, 0, true) 199 | testSendRpc("Client send rpc request(async)", client, true, true, 0, true) 200 | }) 201 | } 202 | 203 | func testSendRpc(testName string, client *baidurpc.RpcClient, timeout, auth bool, chunksize uint32, async bool) { 204 | Convey(testName, func() { 205 | Convey("Test send request EchoService!echo", func() { 206 | doSimpleRPCInvokeWithSignatureWithConvey(client, "EchoService", "echo", false, false, timeout, false, auth, chunksize, async) 207 | }) 208 | Convey("Test send request EchoService!echoWithAttchement", func() { 209 | doSimpleRPCInvokeWithSignatureWithConvey(client, "EchoService", "echoWithAttchement", true, false, timeout, false, auth, chunksize, async) 210 | }) 211 | Convey("Test send request EchoService!echoWithCustomizedError", func() { 212 | doSimpleRPCInvokeWithSignatureWithConvey(client, "EchoService", "echoWithCustomizedError", false, true, timeout, false, auth, chunksize, async) 213 | }) 214 | Convey("Test send request EchoService!echoWithoutContext", func() { 215 | doSimpleRPCInvokeWithSignatureWithConvey(client, "EchoService", "echoWithoutContext", false, false, timeout, false, auth, chunksize, async) 216 | }) 217 | Convey("Test send request EchoService!EchoSlowTest", func() { 218 | doSimpleRPCInvokeWithSignatureWithConvey(client, "EchoService", "EchoSlowTest", false, false, timeout, true, auth, chunksize, async) 219 | }) 220 | }) 221 | } 222 | 223 | // createRpcServer create rpc server by port and localhost 224 | func createRpcServerWithChunkSize(port int, chunksize uint32) *baidurpc.TcpServer { 225 | serverMeta := baidurpc.ServerMeta{} 226 | serverMeta.Port = Int(port) 227 | serverMeta.ChunkSize = chunksize 228 | rpcServer := baidurpc.NewTpcServer(&serverMeta) 229 | return rpcServer 230 | } 231 | 232 | func startRpcServer(chunksize uint32) *baidurpc.TcpServer { 233 | return startRpcServerWithHttpMode(chunksize, false) 234 | } 235 | 236 | // startRpcServer start rpc server and register echo service as default rpc service 237 | func startRpcServerWithHttpMode(chunksize uint32, httpMode bool) *baidurpc.TcpServer { 238 | 239 | rpcServer := createRpcServerWithChunkSize(PORT_1, chunksize) 240 | 241 | echoservice := new(EchoService) 242 | methodMapping := map[string]string{ 243 | "Echo": "echo", 244 | "EchoWithAttchement": "echoWithAttchement", 245 | "EchoWithCustomizedError": "echoWithCustomizedError", 246 | "EchoWithoutContext": "echoWithoutContext", 247 | } 248 | rpcServer.RegisterNameWithMethodMapping("EchoService", echoservice, methodMapping) 249 | 250 | rpcServer.SetTraceService(new(AddOneTraceService)) 251 | if httpMode { 252 | rpcServer.EnableHttp() 253 | } 254 | rpcServer.Start() 255 | 256 | return rpcServer 257 | } 258 | 259 | // createClient 260 | func createClient() (baidurpc.Connection, *baidurpc.RpcClient, error) { 261 | 262 | host := "localhost" 263 | port := PORT_1 264 | 265 | url := baidurpc.URL{} 266 | url.SetHost(&host).SetPort(&port) 267 | 268 | timeout := time.Second * 500 269 | // create client by simple connection 270 | connection, err := baidurpc.NewTCPConnection(url, &timeout) 271 | if err != nil { 272 | return nil, nil, err 273 | } 274 | rpcClient, err := baidurpc.NewRpcCient(connection) 275 | if err != nil { 276 | return nil, nil, err 277 | } 278 | return connection, rpcClient, nil 279 | } 280 | 281 | // createClient 282 | func createPooledConnectionClient() (baidurpc.Connection, *baidurpc.RpcClient, error) { 283 | 284 | host := "localhost" 285 | port := PORT_1 286 | 287 | url := baidurpc.URL{} 288 | url.SetHost(&host).SetPort(&port) 289 | 290 | timeout := time.Second * 5 291 | // create client by simple connection 292 | connection, err := baidurpc.NewTCPConnectionPool(url, &timeout, nil) 293 | if err != nil { 294 | return nil, nil, err 295 | } 296 | rpcClient, err := baidurpc.NewRpcCient(connection) 297 | if err != nil { 298 | return nil, nil, err 299 | } 300 | return connection, rpcClient, nil 301 | } 302 | 303 | // doSimpleRPCInvokeWithSignatureWithConvey send rpc request 304 | func doSimpleRPCInvokeWithSignatureWithConvey(rpcClient *baidurpc.RpcClient, serviceName, methodName string, 305 | withAttachement, withCustomErr, timeout, timeoutCheck, auth bool, chunkSize uint32, async bool) { 306 | Convey("Test Client send rpc request", func() { 307 | rpcInvocation := baidurpc.NewRpcInvocation(&serviceName, &methodName) 308 | 309 | name := "(马林)(matthew)(XML)(jhunters)" 310 | dm := DataMessage{Name: name} 311 | 312 | rpcInvocation.SetParameterIn(&dm) 313 | rpcInvocation.LogId = proto.Int64(1) 314 | rpcInvocation.ChunkSize = chunkSize 315 | rpcInvocation.TraceId = 10 316 | rpcInvocation.SpanId = 11 317 | rpcInvocation.ParentSpanId = 12 318 | rpcInvocation.RpcRequestMetaExt = map[string]string{"key1": "value1"} 319 | 320 | if withAttachement { 321 | rpcInvocation.Attachment = []byte("This is attachment data") 322 | } 323 | 324 | if auth { 325 | rpcInvocation.AuthenticateData = []byte(AUTH_TOKEN) 326 | } 327 | 328 | parameterOut := DataMessage{} 329 | var response *baidurpc.RpcDataPackage 330 | var err error 331 | if timeout { 332 | response, err = rpcClient.SendRpcRequestWithTimeout(1*time.Second, rpcInvocation, ¶meterOut) 333 | if timeoutCheck { 334 | So(err, ShouldNotBeNil) 335 | return 336 | } 337 | } else { 338 | if async { 339 | ch := rpcClient.SendRpcRequestAsyc(rpcInvocation, ¶meterOut) 340 | rpcResult := <-ch 341 | response = rpcResult.GetRpcDataPackage() 342 | err = rpcResult.GetErr() 343 | } else { 344 | response, err = rpcClient.SendRpcRequest(rpcInvocation, ¶meterOut) 345 | } 346 | } 347 | if withCustomErr { 348 | So(err, ShouldNotBeNil) 349 | return 350 | } else { 351 | So(err, ShouldBeNil) 352 | } 353 | So(response, ShouldNotBeNil) 354 | expect := "hello " + name 355 | So(expect, ShouldEqual, parameterOut.Name) 356 | 357 | if withAttachement { 358 | So(string(response.Attachment), ShouldEqual, "I am a attachement, This is attachment data") 359 | } 360 | 361 | So(response.GetTraceId(), ShouldEqual, rpcInvocation.TraceId+1) 362 | So(response.GetParentSpanId(), ShouldEqual, rpcInvocation.ParentSpanId+1) 363 | So(response.GetParentSpanId(), ShouldEqual, rpcInvocation.ParentSpanId+1) 364 | So(response.GetRpcRequestMetaExt()["key1"], ShouldEqual, "value1") 365 | }) 366 | 367 | } 368 | -------------------------------------------------------------------------------- /rpcpackage.go: -------------------------------------------------------------------------------- 1 | // Go support for Protocol Buffers RPC which compatible with https://github.com/Baidu-ecom/Jprotobuf-rpc-socket 2 | // 3 | // Copyright 2002-2007 the original author or authors. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | package baidurpc 17 | 18 | import ( 19 | "bytes" 20 | "encoding/binary" 21 | "errors" 22 | "fmt" 23 | "io" 24 | "log" 25 | "math/rand" 26 | "strings" 27 | 28 | "github.com/golang/snappy" 29 | "google.golang.org/protobuf/proto" 30 | ) 31 | 32 | // error log info definition 33 | var ( 34 | errIgnoreErr = errors.New("[marshal-001]Ingore error") 35 | errMeta = errors.New("[marshal-003]Get nil value from Meta struct after marshal") 36 | LOG_INVALID_BYTES = "[marshal-004]Invalid byte array. maybe a broken byte stream. Received '%b'" 37 | ) 38 | 39 | /* 40 | Data package for baidu RPC. 41 | all request and response data package should apply this. 42 | 43 | ----------------------------------- 44 | | Head | Meta | Data | Attachment | 45 | ----------------------------------- 46 | 47 | 1. with fixed 12 byte length as follow format 48 | ---------------------------------------------- 49 | | PRPC | MessageSize(int32) | MetaSize(int32) | 50 | ---------------------------------------------- 51 | MessageSize = totalSize - 12(Fixed Head Size) 52 | MetaSize = Meta object size 53 | 54 | 2. body proto description as follow 55 | 56 | message RpcMeta { 57 | optional Request request = 1; 58 | optional Response response = 2; 59 | optional int32 compress_type = 3; // 0:nocompress 1:Snappy 2:gzip 60 | optional int64 correlation_id = 4; 61 | optional int32 attachment_size = 5; 62 | optional ChuckInfo chuck_info = 6; 63 | optional bytes authentication_data = 7; 64 | }; 65 | 66 | message Request { 67 | required string service_name = 1; 68 | required string method_name = 2; 69 | optional int64 log_id = 3; 70 | optional int64 traceId=4; 71 | optional int64 spanId=5; 72 | optional int64 parentSpanId=6; 73 | repeat RpcRequestMetaExtField extFields = 7; 74 | }; 75 | 76 | message RpcRequestMetaExtField { 77 | optional string key = 1; 78 | optional string value = 2; 79 | } 80 | 81 | message Response { 82 | optional int32 error_code = 1; 83 | optional string error_text = 2; 84 | }; 85 | 86 | messsage ChuckInfo { 87 | required int64 stream_id = 1; 88 | required int64 chunk_id = 2; 89 | }; 90 | 91 | 3. customize transport data message. 92 | 93 | 4. attachment body data message 94 | */ 95 | type RpcDataPackage struct { 96 | Head *Header // rpc head 97 | Meta *RpcMeta // rpc meta 98 | Data []byte 99 | Attachment []byte 100 | 101 | // private field 102 | chunkSize uint32 103 | } 104 | 105 | // NewRpcDataPackage returns a new RpcDataPackage and init all fields 106 | func NewRpcDataPackage() *RpcDataPackage { 107 | data := RpcDataPackage{} 108 | doInit(&data) 109 | 110 | data.GetMeta().Response = &Response{} 111 | 112 | return &data 113 | } 114 | 115 | // Clear to clear and init all fields 116 | func (r *RpcDataPackage) Clear() { 117 | // r.Head = &Header{} 118 | // r.Meta = &RpcMeta{} 119 | request := r.Meta.Request 120 | if request == nil { 121 | r.Meta.Request = &Request{} 122 | } 123 | response := r.Meta.Response 124 | if response == nil { 125 | r.Meta.Response = &Response{} 126 | } 127 | r.Data = nil 128 | r.Attachment = nil 129 | r.ClearChunkStatus() 130 | } 131 | 132 | // MagicCode set magic code field 133 | func (r *RpcDataPackage) MagicCode(magicCode string) { 134 | if len(magicCode) != 4 { 135 | return 136 | } 137 | 138 | initHeader(r) 139 | r.Head.SetMagicCode([]byte(magicCode)) 140 | 141 | } 142 | 143 | // GetMagicCode return magic code value 144 | func (r *RpcDataPackage) GetMagicCode() string { 145 | initHeader(r) 146 | return string(r.Head.GetMagicCode()) 147 | 148 | } 149 | 150 | func initHeader(r *RpcDataPackage) { 151 | if r.Head == nil { 152 | r.Head = &Header{} 153 | } 154 | } 155 | 156 | func initRpcMeta(r *RpcDataPackage) { 157 | if r.Meta == nil { 158 | r.Meta = &RpcMeta{} 159 | } 160 | } 161 | 162 | func initChuckInfo(r *RpcDataPackage) { 163 | initRpcMeta(r) 164 | if r.Meta.ChuckInfo == nil { 165 | r.Meta.ChuckInfo = &ChunkInfo{} 166 | } 167 | } 168 | 169 | func initRequest(r *RpcDataPackage) { 170 | initRpcMeta(r) 171 | 172 | request := r.Meta.Request 173 | if request == nil { 174 | r.Meta.Request = &Request{} 175 | } 176 | 177 | } 178 | 179 | func initResponse(r *RpcDataPackage) { 180 | initRpcMeta(r) 181 | 182 | response := r.Meta.Response 183 | if response == nil { 184 | r.Meta.Response = &Response{} 185 | } 186 | 187 | } 188 | 189 | // ServiceName set service name field 190 | func (r *RpcDataPackage) ServiceName(serviceName string) *RpcDataPackage { 191 | initRequest(r) 192 | 193 | r.Meta.Request.ServiceName = serviceName 194 | 195 | return r 196 | } 197 | 198 | // MethodName set method name field 199 | func (r *RpcDataPackage) MethodName(methodName string) *RpcDataPackage { 200 | initRequest(r) 201 | 202 | r.Meta.Request.MethodName = methodName 203 | 204 | return r 205 | } 206 | 207 | // SetData set data 208 | func (r *RpcDataPackage) SetData(Data []byte) *RpcDataPackage { 209 | r.Data = Data 210 | return r 211 | } 212 | 213 | // SetAttachment set attachment 214 | func (r *RpcDataPackage) SetAttachment(Attachment []byte) *RpcDataPackage { 215 | r.Attachment = Attachment 216 | return r 217 | } 218 | 219 | // AuthenticationData set authentication data 220 | func (r *RpcDataPackage) AuthenticationData(authenticationData []byte) *RpcDataPackage { 221 | initRpcMeta(r) 222 | 223 | r.Meta.AuthenticationData = authenticationData 224 | return r 225 | } 226 | 227 | // CorrelationId set correlationId data 228 | func (r *RpcDataPackage) CorrelationId(correlationId int64) *RpcDataPackage { 229 | initRpcMeta(r) 230 | 231 | r.Meta.CorrelationId = correlationId 232 | return r 233 | } 234 | 235 | // CompressType set compress type data 236 | func (r *RpcDataPackage) CompressType(compressType int32) *RpcDataPackage { 237 | initRpcMeta(r) 238 | 239 | r.Meta.CompressType = compressType 240 | return r 241 | } 242 | 243 | // LogId set log id data 244 | func (r *RpcDataPackage) LogId(logId int64) *RpcDataPackage { 245 | initRequest(r) 246 | 247 | r.Meta.Request.LogId = logId 248 | 249 | return r 250 | } 251 | 252 | // GetLogId return log id 253 | func (r *RpcDataPackage) GetLogId() int64 { 254 | initRequest(r) 255 | return r.Meta.Request.GetLogId() 256 | } 257 | 258 | // TraceId set trace id data 259 | func (r *RpcDataPackage) TraceId(traceId int64) *RpcDataPackage { 260 | initRequest(r) 261 | r.Meta.Request.TraceId = traceId 262 | return r 263 | } 264 | 265 | // GetTraceId return trace id 266 | func (r *RpcDataPackage) GetTraceId() int64 { 267 | initRequest(r) 268 | return r.Meta.Request.TraceId 269 | } 270 | 271 | // SpanId set span id 272 | func (r *RpcDataPackage) SpanId(spanId int64) *RpcDataPackage { 273 | initRequest(r) 274 | r.Meta.Request.SpanId = spanId 275 | return r 276 | } 277 | 278 | // GetSpanId return span id 279 | func (r *RpcDataPackage) GetSpanId() int64 { 280 | initRequest(r) 281 | return r.Meta.Request.SpanId 282 | } 283 | 284 | // ParentSpanId set parent span id 285 | func (r *RpcDataPackage) ParentSpanId(parentSpanId int64) *RpcDataPackage { 286 | initRequest(r) 287 | r.Meta.Request.ParentSpanId = parentSpanId 288 | return r 289 | } 290 | 291 | // GetParentSpanId return parent span id 292 | func (r *RpcDataPackage) GetParentSpanId() int64 { 293 | initRequest(r) 294 | return r.Meta.Request.ParentSpanId 295 | } 296 | 297 | // RpcRequestMetaExt set rpc request meta extendsion fields 298 | func (r *RpcDataPackage) RpcRequestMetaExt(ext map[string]string) *RpcDataPackage { 299 | initRequest(r) 300 | extMap := make([]*RpcRequestMetaExtField, 0) 301 | for key, value := range ext { 302 | extfield := &RpcRequestMetaExtField{Key: key, Value: value} 303 | extMap = append(extMap, extfield) 304 | } 305 | r.Meta.Request.RpcRequestMetaExt = extMap 306 | return r 307 | } 308 | 309 | // GetRpcRequestMetaExt return rpc request meta extendstion 310 | func (r *RpcDataPackage) GetRpcRequestMetaExt() map[string]string { 311 | initRequest(r) 312 | ret := make(map[string]string) 313 | for _, rr := range r.Meta.Request.RpcRequestMetaExt { 314 | ret[rr.Key] = rr.Value 315 | } 316 | 317 | return ret 318 | } 319 | 320 | // ErrorCode set error code field 321 | func (r *RpcDataPackage) ErrorCode(errorCode int32) *RpcDataPackage { 322 | initResponse(r) 323 | 324 | r.Meta.Response.ErrorCode = errorCode 325 | 326 | return r 327 | } 328 | 329 | // ErrorText set error text field 330 | func (r *RpcDataPackage) ErrorText(errorText string) *RpcDataPackage { 331 | initResponse(r) 332 | 333 | r.Meta.Response.ErrorText = errorText 334 | 335 | return r 336 | } 337 | 338 | // ExtraParams set extra parameters field 339 | func (r *RpcDataPackage) ExtraParams(extraParams []byte) *RpcDataPackage { 340 | initRequest(r) 341 | 342 | r.Meta.Request.ExtraParam = extraParams 343 | 344 | return r 345 | } 346 | 347 | // ChuckInfo set chuck info 348 | func (r *RpcDataPackage) ChuckInfo(streamId int64, chunkId int64) *RpcDataPackage { 349 | ChuckInfo := ChunkInfo{} 350 | ChuckInfo.StreamId = streamId 351 | ChuckInfo.ChunkId = chunkId 352 | initRpcMeta(r) 353 | r.Meta.ChuckInfo = &ChuckInfo 354 | return r 355 | } 356 | 357 | func doInit(r *RpcDataPackage) { 358 | initHeader(r) 359 | initRequest(r) 360 | initResponse(r) 361 | } 362 | 363 | // GetHead return Header data 364 | func (r *RpcDataPackage) GetHead() *Header { 365 | if r.Head == nil { 366 | return nil 367 | } 368 | return r.Head 369 | } 370 | 371 | // GetMeta return RpcMeta data 372 | func (r *RpcDataPackage) GetMeta() *RpcMeta { 373 | if r.Meta == nil { 374 | return nil 375 | } 376 | return r.Meta 377 | } 378 | 379 | // GetData return data field 380 | func (r *RpcDataPackage) GetData() []byte { 381 | return r.Data 382 | } 383 | 384 | // GetAttachment return attachment field 385 | func (r *RpcDataPackage) GetAttachment() []byte { 386 | return r.Attachment 387 | } 388 | 389 | /* 390 | Convert RpcPackage to byte array 391 | */ 392 | func (r *RpcDataPackage) WriteIO(rw io.Writer) error { 393 | 394 | bytes, err := r.Write() 395 | if err != nil { 396 | return err 397 | } 398 | 399 | _, err = rw.Write(bytes) 400 | if err != nil { 401 | return err 402 | } 403 | 404 | return nil 405 | } 406 | 407 | /* 408 | Convert RpcPackage to byte array 409 | */ 410 | func (r *RpcDataPackage) Write() ([]byte, error) { 411 | doInit(r) 412 | 413 | var totalSize int32 = 0 414 | var dataSize int32 = 0 415 | var err error 416 | if r.Data != nil { 417 | compressType := r.GetMeta().GetCompressType() 418 | if compressType == COMPRESS_GZIP { 419 | r.Data, err = GZIP(r.Data) 420 | if err != nil { 421 | return nil, err 422 | } 423 | } else if compressType == COMPRESS_SNAPPY { 424 | dst := make([]byte, snappy.MaxEncodedLen(len(r.Data))) 425 | r.Data = snappy.Encode(dst, r.Data) 426 | } 427 | 428 | dataSize = int32(len(r.Data)) 429 | totalSize = totalSize + dataSize 430 | } 431 | 432 | var attachmentSize int32 = 0 433 | if r.Attachment != nil { 434 | attachmentSize = int32(len(r.Attachment)) 435 | totalSize = totalSize + attachmentSize 436 | } 437 | 438 | r.Meta.AttachmentSize = int32(attachmentSize) 439 | 440 | metaBytes, err := proto.Marshal(r.Meta) 441 | if err != nil { 442 | return nil, err 443 | } 444 | 445 | if metaBytes == nil { 446 | return nil, errMeta 447 | } 448 | 449 | rpcMetaSize := int32(len(metaBytes)) 450 | totalSize = totalSize + rpcMetaSize 451 | 452 | r.Head.MessageSize = int32(totalSize) 453 | r.Head.MetaSize = int32(rpcMetaSize) 454 | buf := new(bytes.Buffer) 455 | 456 | headBytes, _ := r.Head.Write() 457 | binary.Write(buf, binary.BigEndian, headBytes) 458 | binary.Write(buf, binary.BigEndian, metaBytes) 459 | 460 | if r.Data != nil { 461 | binary.Write(buf, binary.BigEndian, r.Data) 462 | } 463 | 464 | if r.Attachment != nil { 465 | binary.Write(buf, binary.BigEndian, r.Attachment) 466 | } 467 | 468 | return buf.Bytes(), nil 469 | } 470 | 471 | /* 472 | Read byte array and initialize RpcPackage 473 | */ 474 | func (r *RpcDataPackage) ReadIO(rw io.Reader) error { 475 | if rw == nil { 476 | return errors.New("bytes is nil") 477 | } 478 | 479 | doInit(r) 480 | 481 | // read Head 482 | head := make([]byte, SIZE) 483 | 484 | _, err := io.ReadFull(rw, head) 485 | if err != nil { 486 | if err == io.EOF { 487 | return errIgnoreErr 488 | } 489 | log.Println("Read head error", err) 490 | // only to close current connection 491 | return err 492 | } 493 | 494 | // unmarshal Head message 495 | r.Head.Read(head) 496 | if strings.Compare(string(r.Head.MagicCode), MAGIC_CODE) != 0 { 497 | return fmt.Errorf("invalid magic code '%v'", string(r.Head.MagicCode)) 498 | } 499 | 500 | // get RPC Meta size 501 | metaSize := r.Head.GetMetaSize() 502 | totalSize := r.Head.GetMessageSize() 503 | if totalSize <= 0 { 504 | // maybe heart beat data message, so do ignore here 505 | return errIgnoreErr 506 | } 507 | 508 | // read left 509 | leftSize := totalSize 510 | body := make([]byte, leftSize) 511 | 512 | _, err = io.ReadFull(rw, body) 513 | if err != nil { 514 | return fmt.Errorf("Read body error %w ", err) 515 | } 516 | 517 | proto.Unmarshal(body[0:metaSize], r.Meta) 518 | 519 | attachmentSize := r.Meta.GetAttachmentSize() 520 | dataSize := leftSize - metaSize - attachmentSize 521 | 522 | dataOffset := metaSize 523 | if dataSize > 0 { 524 | dataOffset = dataSize + metaSize 525 | r.Data = body[metaSize:dataOffset] 526 | 527 | compressType := r.GetMeta().GetCompressType() 528 | if compressType == COMPRESS_GZIP { 529 | r.Data, err = GUNZIP(r.Data) 530 | 531 | if err != nil { 532 | return err 533 | } 534 | } else if compressType == COMPRESS_SNAPPY { 535 | dst := make([]byte, 1) 536 | r.Data, err = snappy.Decode(dst, r.Data) 537 | if err != nil { 538 | return err 539 | } 540 | } 541 | } 542 | // if need read Attachment 543 | if attachmentSize > 0 { 544 | r.Attachment = body[dataOffset:leftSize] 545 | } 546 | 547 | return nil 548 | } 549 | 550 | // Read and parse data from target byte slice 551 | func (r *RpcDataPackage) Read(b []byte) error { 552 | 553 | if b == nil { 554 | return errors.New("parameter 'b' is nil") 555 | } 556 | 557 | buf := bytes.NewBuffer(b) 558 | 559 | return r.ReadIO(buf) 560 | 561 | } 562 | 563 | // Chunk chunk to small packages by chunk size 564 | func (r *RpcDataPackage) Chunk(chunkSize int) []*RpcDataPackage { 565 | if chunkSize <= 0 { 566 | return []*RpcDataPackage{r} 567 | } 568 | 569 | dataSize := len(r.Data) 570 | chunkCount := dataSize / chunkSize 571 | if dataSize%chunkSize != 0 { 572 | chunkCount++ 573 | } 574 | if chunkCount == 1 { 575 | return []*RpcDataPackage{r} 576 | } 577 | 578 | ret := make([]*RpcDataPackage, chunkCount) 579 | startPos := 0 580 | chunkStreamID := rand.Int63() 581 | 582 | for i := 0; i < chunkCount; i++ { 583 | temp := *r 584 | base := &temp // copy value 585 | 586 | tempMeta := *base.Meta 587 | base.Meta = &tempMeta 588 | initChuckInfo(base) 589 | offset := startPos + chunkSize 590 | if offset > dataSize { 591 | offset = dataSize 592 | } 593 | base.Data = base.Data[startPos:offset] 594 | startPos += chunkSize 595 | tempChuckInfo := &ChunkInfo{StreamId: chunkStreamID, ChunkId: int64(i + 1)} 596 | if i == chunkCount-1 { 597 | // this is last package 598 | tempChuckInfo.ChunkId = int64(-1) 599 | } 600 | if i > 0 { 601 | // fix duplicate attachment data 602 | base.Attachment = nil 603 | } 604 | ChuckInfo := *tempChuckInfo 605 | base.Meta.ChuckInfo = &ChuckInfo 606 | ret[i] = base 607 | } 608 | 609 | return ret 610 | } 611 | 612 | // GetChunkStreamId return chunk stream id 613 | func (r *RpcDataPackage) GetChunkStreamId() int64 { 614 | initRpcMeta(r) 615 | return r.Meta.ChuckInfo.GetStreamId() 616 | } 617 | 618 | // getChunkId 619 | func (r *RpcDataPackage) getChunkId() int64 { 620 | initRpcMeta(r) 621 | return r.Meta.ChuckInfo.GetChunkId() 622 | } 623 | 624 | // IsChunkPackage return if chunk package type 625 | func (r *RpcDataPackage) IsChunkPackage() bool { 626 | return r.GetChunkStreamId() != 0 627 | } 628 | 629 | // IsFinalPackage return if the final chunk package 630 | func (r *RpcDataPackage) IsFinalPackage() bool { 631 | return r.getChunkId() == -1 632 | } 633 | 634 | // ClearChunkStatus to clear chunk status 635 | func (r *RpcDataPackage) ClearChunkStatus() { 636 | r.ChuckInfo(0, 0) 637 | } 638 | -------------------------------------------------------------------------------- /pb_data.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go v1.26.0 4 | // protoc v3.9.2 5 | // source: brpc_meta.proto 6 | 7 | package baidurpc 8 | 9 | import ( 10 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 11 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 12 | reflect "reflect" 13 | sync "sync" 14 | ) 15 | 16 | const ( 17 | // Verify that this generated code is sufficiently up-to-date. 18 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 19 | // Verify that runtime/protoimpl is sufficiently up-to-date. 20 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 21 | ) 22 | 23 | type RpcMeta struct { 24 | state protoimpl.MessageState 25 | sizeCache protoimpl.SizeCache 26 | unknownFields protoimpl.UnknownFields 27 | 28 | Request *Request `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` 29 | Response *Response `protobuf:"bytes,2,opt,name=response,proto3" json:"response,omitempty"` 30 | CompressType int32 `protobuf:"varint,3,opt,name=compress_type,json=compressType,proto3" json:"compress_type,omitempty"` // 0:nocompress 1:Snappy 2:gzip 31 | CorrelationId int64 `protobuf:"varint,4,opt,name=correlation_id,json=correlationId,proto3" json:"correlation_id,omitempty"` 32 | AttachmentSize int32 `protobuf:"varint,5,opt,name=attachment_size,json=attachmentSize,proto3" json:"attachment_size,omitempty"` 33 | ChuckInfo *ChunkInfo `protobuf:"bytes,6,opt,name=chuck_info,json=chuckInfo,proto3" json:"chuck_info,omitempty"` 34 | AuthenticationData []byte `protobuf:"bytes,7,opt,name=authentication_data,json=authenticationData,proto3" json:"authentication_data,omitempty"` 35 | } 36 | 37 | func (x *RpcMeta) Reset() { 38 | *x = RpcMeta{} 39 | if protoimpl.UnsafeEnabled { 40 | mi := &file_brpc_meta_proto_msgTypes[0] 41 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 42 | ms.StoreMessageInfo(mi) 43 | } 44 | } 45 | 46 | func (x *RpcMeta) String() string { 47 | return protoimpl.X.MessageStringOf(x) 48 | } 49 | 50 | func (*RpcMeta) ProtoMessage() {} 51 | 52 | func (x *RpcMeta) ProtoReflect() protoreflect.Message { 53 | mi := &file_brpc_meta_proto_msgTypes[0] 54 | if protoimpl.UnsafeEnabled && x != nil { 55 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 56 | if ms.LoadMessageInfo() == nil { 57 | ms.StoreMessageInfo(mi) 58 | } 59 | return ms 60 | } 61 | return mi.MessageOf(x) 62 | } 63 | 64 | // Deprecated: Use RpcMeta.ProtoReflect.Descriptor instead. 65 | func (*RpcMeta) Descriptor() ([]byte, []int) { 66 | return file_brpc_meta_proto_rawDescGZIP(), []int{0} 67 | } 68 | 69 | func (x *RpcMeta) GetRequest() *Request { 70 | if x != nil { 71 | return x.Request 72 | } 73 | return nil 74 | } 75 | 76 | func (x *RpcMeta) GetResponse() *Response { 77 | if x != nil { 78 | return x.Response 79 | } 80 | return nil 81 | } 82 | 83 | func (x *RpcMeta) GetCompressType() int32 { 84 | if x != nil { 85 | return x.CompressType 86 | } 87 | return 0 88 | } 89 | 90 | func (x *RpcMeta) GetCorrelationId() int64 { 91 | if x != nil { 92 | return x.CorrelationId 93 | } 94 | return 0 95 | } 96 | 97 | func (x *RpcMeta) GetAttachmentSize() int32 { 98 | if x != nil { 99 | return x.AttachmentSize 100 | } 101 | return 0 102 | } 103 | 104 | func (x *RpcMeta) GetChuckInfo() *ChunkInfo { 105 | if x != nil { 106 | return x.ChuckInfo 107 | } 108 | return nil 109 | } 110 | 111 | func (x *RpcMeta) GetAuthenticationData() []byte { 112 | if x != nil { 113 | return x.AuthenticationData 114 | } 115 | return nil 116 | } 117 | 118 | type Request struct { 119 | state protoimpl.MessageState 120 | sizeCache protoimpl.SizeCache 121 | unknownFields protoimpl.UnknownFields 122 | 123 | ServiceName string `protobuf:"bytes,1,opt,name=service_name,json=serviceName,proto3" json:"service_name,omitempty"` 124 | MethodName string `protobuf:"bytes,2,opt,name=method_name,json=methodName,proto3" json:"method_name,omitempty"` 125 | LogId int64 `protobuf:"varint,3,opt,name=log_id,json=logId,proto3" json:"log_id,omitempty"` 126 | TraceId int64 `protobuf:"varint,4,opt,name=traceId,proto3" json:"traceId,omitempty"` 127 | SpanId int64 `protobuf:"varint,5,opt,name=spanId,proto3" json:"spanId,omitempty"` 128 | ParentSpanId int64 `protobuf:"varint,6,opt,name=parentSpanId,proto3" json:"parentSpanId,omitempty"` 129 | RpcRequestMetaExt []*RpcRequestMetaExtField `protobuf:"bytes,7,rep,name=rpcRequestMetaExt,proto3" json:"rpcRequestMetaExt,omitempty"` 130 | ExtraParam []byte `protobuf:"bytes,110,opt,name=extraParam,proto3" json:"extraParam,omitempty"` 131 | } 132 | 133 | func (x *Request) Reset() { 134 | *x = Request{} 135 | if protoimpl.UnsafeEnabled { 136 | mi := &file_brpc_meta_proto_msgTypes[1] 137 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 138 | ms.StoreMessageInfo(mi) 139 | } 140 | } 141 | 142 | func (x *Request) String() string { 143 | return protoimpl.X.MessageStringOf(x) 144 | } 145 | 146 | func (*Request) ProtoMessage() {} 147 | 148 | func (x *Request) ProtoReflect() protoreflect.Message { 149 | mi := &file_brpc_meta_proto_msgTypes[1] 150 | if protoimpl.UnsafeEnabled && x != nil { 151 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 152 | if ms.LoadMessageInfo() == nil { 153 | ms.StoreMessageInfo(mi) 154 | } 155 | return ms 156 | } 157 | return mi.MessageOf(x) 158 | } 159 | 160 | // Deprecated: Use Request.ProtoReflect.Descriptor instead. 161 | func (*Request) Descriptor() ([]byte, []int) { 162 | return file_brpc_meta_proto_rawDescGZIP(), []int{1} 163 | } 164 | 165 | func (x *Request) GetServiceName() string { 166 | if x != nil { 167 | return x.ServiceName 168 | } 169 | return "" 170 | } 171 | 172 | func (x *Request) GetMethodName() string { 173 | if x != nil { 174 | return x.MethodName 175 | } 176 | return "" 177 | } 178 | 179 | func (x *Request) GetLogId() int64 { 180 | if x != nil { 181 | return x.LogId 182 | } 183 | return 0 184 | } 185 | 186 | func (x *Request) GetTraceId() int64 { 187 | if x != nil { 188 | return x.TraceId 189 | } 190 | return 0 191 | } 192 | 193 | func (x *Request) GetSpanId() int64 { 194 | if x != nil { 195 | return x.SpanId 196 | } 197 | return 0 198 | } 199 | 200 | func (x *Request) GetParentSpanId() int64 { 201 | if x != nil { 202 | return x.ParentSpanId 203 | } 204 | return 0 205 | } 206 | 207 | func (x *Request) GetRpcRequestMetaExt() []*RpcRequestMetaExtField { 208 | if x != nil { 209 | return x.RpcRequestMetaExt 210 | } 211 | return nil 212 | } 213 | 214 | func (x *Request) GetExtraParam() []byte { 215 | if x != nil { 216 | return x.ExtraParam 217 | } 218 | return nil 219 | } 220 | 221 | type Response struct { 222 | state protoimpl.MessageState 223 | sizeCache protoimpl.SizeCache 224 | unknownFields protoimpl.UnknownFields 225 | 226 | ErrorCode int32 `protobuf:"varint,1,opt,name=error_code,json=errorCode,proto3" json:"error_code,omitempty"` 227 | ErrorText string `protobuf:"bytes,2,opt,name=error_text,json=errorText,proto3" json:"error_text,omitempty"` 228 | } 229 | 230 | func (x *Response) Reset() { 231 | *x = Response{} 232 | if protoimpl.UnsafeEnabled { 233 | mi := &file_brpc_meta_proto_msgTypes[2] 234 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 235 | ms.StoreMessageInfo(mi) 236 | } 237 | } 238 | 239 | func (x *Response) String() string { 240 | return protoimpl.X.MessageStringOf(x) 241 | } 242 | 243 | func (*Response) ProtoMessage() {} 244 | 245 | func (x *Response) ProtoReflect() protoreflect.Message { 246 | mi := &file_brpc_meta_proto_msgTypes[2] 247 | if protoimpl.UnsafeEnabled && x != nil { 248 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 249 | if ms.LoadMessageInfo() == nil { 250 | ms.StoreMessageInfo(mi) 251 | } 252 | return ms 253 | } 254 | return mi.MessageOf(x) 255 | } 256 | 257 | // Deprecated: Use Response.ProtoReflect.Descriptor instead. 258 | func (*Response) Descriptor() ([]byte, []int) { 259 | return file_brpc_meta_proto_rawDescGZIP(), []int{2} 260 | } 261 | 262 | func (x *Response) GetErrorCode() int32 { 263 | if x != nil { 264 | return x.ErrorCode 265 | } 266 | return 0 267 | } 268 | 269 | func (x *Response) GetErrorText() string { 270 | if x != nil { 271 | return x.ErrorText 272 | } 273 | return "" 274 | } 275 | 276 | type ChunkInfo struct { 277 | state protoimpl.MessageState 278 | sizeCache protoimpl.SizeCache 279 | unknownFields protoimpl.UnknownFields 280 | 281 | StreamId int64 `protobuf:"varint,1,opt,name=stream_id,json=streamId,proto3" json:"stream_id,omitempty"` 282 | ChunkId int64 `protobuf:"varint,2,opt,name=chunk_id,json=chunkId,proto3" json:"chunk_id,omitempty"` 283 | } 284 | 285 | func (x *ChunkInfo) Reset() { 286 | *x = ChunkInfo{} 287 | if protoimpl.UnsafeEnabled { 288 | mi := &file_brpc_meta_proto_msgTypes[3] 289 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 290 | ms.StoreMessageInfo(mi) 291 | } 292 | } 293 | 294 | func (x *ChunkInfo) String() string { 295 | return protoimpl.X.MessageStringOf(x) 296 | } 297 | 298 | func (*ChunkInfo) ProtoMessage() {} 299 | 300 | func (x *ChunkInfo) ProtoReflect() protoreflect.Message { 301 | mi := &file_brpc_meta_proto_msgTypes[3] 302 | if protoimpl.UnsafeEnabled && x != nil { 303 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 304 | if ms.LoadMessageInfo() == nil { 305 | ms.StoreMessageInfo(mi) 306 | } 307 | return ms 308 | } 309 | return mi.MessageOf(x) 310 | } 311 | 312 | // Deprecated: Use ChunkInfo.ProtoReflect.Descriptor instead. 313 | func (*ChunkInfo) Descriptor() ([]byte, []int) { 314 | return file_brpc_meta_proto_rawDescGZIP(), []int{3} 315 | } 316 | 317 | func (x *ChunkInfo) GetStreamId() int64 { 318 | if x != nil { 319 | return x.StreamId 320 | } 321 | return 0 322 | } 323 | 324 | func (x *ChunkInfo) GetChunkId() int64 { 325 | if x != nil { 326 | return x.ChunkId 327 | } 328 | return 0 329 | } 330 | 331 | type RpcRequestMetaExtField struct { 332 | state protoimpl.MessageState 333 | sizeCache protoimpl.SizeCache 334 | unknownFields protoimpl.UnknownFields 335 | 336 | Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` 337 | Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` 338 | } 339 | 340 | func (x *RpcRequestMetaExtField) Reset() { 341 | *x = RpcRequestMetaExtField{} 342 | if protoimpl.UnsafeEnabled { 343 | mi := &file_brpc_meta_proto_msgTypes[4] 344 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 345 | ms.StoreMessageInfo(mi) 346 | } 347 | } 348 | 349 | func (x *RpcRequestMetaExtField) String() string { 350 | return protoimpl.X.MessageStringOf(x) 351 | } 352 | 353 | func (*RpcRequestMetaExtField) ProtoMessage() {} 354 | 355 | func (x *RpcRequestMetaExtField) ProtoReflect() protoreflect.Message { 356 | mi := &file_brpc_meta_proto_msgTypes[4] 357 | if protoimpl.UnsafeEnabled && x != nil { 358 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 359 | if ms.LoadMessageInfo() == nil { 360 | ms.StoreMessageInfo(mi) 361 | } 362 | return ms 363 | } 364 | return mi.MessageOf(x) 365 | } 366 | 367 | // Deprecated: Use RpcRequestMetaExtField.ProtoReflect.Descriptor instead. 368 | func (*RpcRequestMetaExtField) Descriptor() ([]byte, []int) { 369 | return file_brpc_meta_proto_rawDescGZIP(), []int{4} 370 | } 371 | 372 | func (x *RpcRequestMetaExtField) GetKey() string { 373 | if x != nil { 374 | return x.Key 375 | } 376 | return "" 377 | } 378 | 379 | func (x *RpcRequestMetaExtField) GetValue() string { 380 | if x != nil { 381 | return x.Value 382 | } 383 | return "" 384 | } 385 | 386 | var File_brpc_meta_proto protoreflect.FileDescriptor 387 | 388 | var file_brpc_meta_proto_rawDesc = []byte{ 389 | 0x0a, 0x0f, 0x62, 0x72, 0x70, 0x63, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 390 | 0x6f, 0x22, 0xa5, 0x02, 0x0a, 0x07, 0x52, 0x70, 0x63, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x22, 0x0a, 391 | 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 392 | 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 393 | 0x74, 0x12, 0x25, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x02, 0x20, 394 | 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, 395 | 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x70, 396 | 0x72, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 397 | 0x0c, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x25, 0x0a, 398 | 0x0e, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 399 | 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 400 | 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 401 | 0x6e, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x61, 402 | 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x29, 0x0a, 403 | 0x0a, 0x63, 0x68, 0x75, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 404 | 0x0b, 0x32, 0x0a, 0x2e, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x63, 405 | 0x68, 0x75, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2f, 0x0a, 0x13, 0x61, 0x75, 0x74, 0x68, 406 | 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 407 | 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 408 | 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x22, 0xa1, 0x02, 0x0a, 0x07, 0x52, 0x65, 409 | 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 410 | 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 411 | 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x68, 412 | 0x6f, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 413 | 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x6c, 0x6f, 0x67, 414 | 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x6f, 0x67, 0x49, 0x64, 415 | 0x12, 0x18, 0x0a, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 416 | 0x03, 0x52, 0x07, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x70, 417 | 0x61, 0x6e, 0x49, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x73, 0x70, 0x61, 0x6e, 418 | 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x61, 0x6e, 419 | 0x49, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 420 | 0x53, 0x70, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x45, 0x0a, 0x11, 0x72, 0x70, 0x63, 0x52, 0x65, 0x71, 421 | 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x78, 0x74, 0x18, 0x07, 0x20, 0x03, 0x28, 422 | 0x0b, 0x32, 0x17, 0x2e, 0x52, 0x70, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 423 | 0x74, 0x61, 0x45, 0x78, 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x11, 0x72, 0x70, 0x63, 0x52, 424 | 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x78, 0x74, 0x12, 0x1e, 0x0a, 425 | 0x0a, 0x65, 0x78, 0x74, 0x72, 0x61, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x18, 0x6e, 0x20, 0x01, 0x28, 426 | 0x0c, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x72, 0x61, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x22, 0x48, 0x0a, 427 | 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x72, 0x72, 428 | 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x65, 429 | 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x72, 0x72, 0x6f, 430 | 0x72, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x72, 431 | 0x72, 0x6f, 0x72, 0x54, 0x65, 0x78, 0x74, 0x22, 0x43, 0x0a, 0x09, 0x43, 0x68, 0x75, 0x6e, 0x6b, 432 | 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x69, 433 | 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 434 | 0x64, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 435 | 0x01, 0x28, 0x03, 0x52, 0x07, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x49, 0x64, 0x22, 0x40, 0x0a, 0x16, 436 | 0x52, 0x70, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x78, 437 | 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 438 | 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 439 | 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x22, 440 | 0x5a, 0x20, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x61, 0x69, 441 | 0x64, 0x75, 0x2d, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x62, 0x61, 0x69, 0x64, 0x75, 0x72, 442 | 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 443 | } 444 | 445 | var ( 446 | file_brpc_meta_proto_rawDescOnce sync.Once 447 | file_brpc_meta_proto_rawDescData = file_brpc_meta_proto_rawDesc 448 | ) 449 | 450 | func file_brpc_meta_proto_rawDescGZIP() []byte { 451 | file_brpc_meta_proto_rawDescOnce.Do(func() { 452 | file_brpc_meta_proto_rawDescData = protoimpl.X.CompressGZIP(file_brpc_meta_proto_rawDescData) 453 | }) 454 | return file_brpc_meta_proto_rawDescData 455 | } 456 | 457 | var file_brpc_meta_proto_msgTypes = make([]protoimpl.MessageInfo, 5) 458 | var file_brpc_meta_proto_goTypes = []interface{}{ 459 | (*RpcMeta)(nil), // 0: RpcMeta 460 | (*Request)(nil), // 1: Request 461 | (*Response)(nil), // 2: Response 462 | (*ChunkInfo)(nil), // 3: ChunkInfo 463 | (*RpcRequestMetaExtField)(nil), // 4: RpcRequestMetaExtField 464 | } 465 | var file_brpc_meta_proto_depIdxs = []int32{ 466 | 1, // 0: RpcMeta.request:type_name -> Request 467 | 2, // 1: RpcMeta.response:type_name -> Response 468 | 3, // 2: RpcMeta.chuck_info:type_name -> ChunkInfo 469 | 4, // 3: Request.rpcRequestMetaExt:type_name -> RpcRequestMetaExtField 470 | 4, // [4:4] is the sub-list for method output_type 471 | 4, // [4:4] is the sub-list for method input_type 472 | 4, // [4:4] is the sub-list for extension type_name 473 | 4, // [4:4] is the sub-list for extension extendee 474 | 0, // [0:4] is the sub-list for field type_name 475 | } 476 | 477 | func init() { file_brpc_meta_proto_init() } 478 | func file_brpc_meta_proto_init() { 479 | if File_brpc_meta_proto != nil { 480 | return 481 | } 482 | if !protoimpl.UnsafeEnabled { 483 | file_brpc_meta_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 484 | switch v := v.(*RpcMeta); i { 485 | case 0: 486 | return &v.state 487 | case 1: 488 | return &v.sizeCache 489 | case 2: 490 | return &v.unknownFields 491 | default: 492 | return nil 493 | } 494 | } 495 | file_brpc_meta_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { 496 | switch v := v.(*Request); i { 497 | case 0: 498 | return &v.state 499 | case 1: 500 | return &v.sizeCache 501 | case 2: 502 | return &v.unknownFields 503 | default: 504 | return nil 505 | } 506 | } 507 | file_brpc_meta_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { 508 | switch v := v.(*Response); i { 509 | case 0: 510 | return &v.state 511 | case 1: 512 | return &v.sizeCache 513 | case 2: 514 | return &v.unknownFields 515 | default: 516 | return nil 517 | } 518 | } 519 | file_brpc_meta_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { 520 | switch v := v.(*ChunkInfo); i { 521 | case 0: 522 | return &v.state 523 | case 1: 524 | return &v.sizeCache 525 | case 2: 526 | return &v.unknownFields 527 | default: 528 | return nil 529 | } 530 | } 531 | file_brpc_meta_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { 532 | switch v := v.(*RpcRequestMetaExtField); i { 533 | case 0: 534 | return &v.state 535 | case 1: 536 | return &v.sizeCache 537 | case 2: 538 | return &v.unknownFields 539 | default: 540 | return nil 541 | } 542 | } 543 | } 544 | type x struct{} 545 | out := protoimpl.TypeBuilder{ 546 | File: protoimpl.DescBuilder{ 547 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 548 | RawDescriptor: file_brpc_meta_proto_rawDesc, 549 | NumEnums: 0, 550 | NumMessages: 5, 551 | NumExtensions: 0, 552 | NumServices: 0, 553 | }, 554 | GoTypes: file_brpc_meta_proto_goTypes, 555 | DependencyIndexes: file_brpc_meta_proto_depIdxs, 556 | MessageInfos: file_brpc_meta_proto_msgTypes, 557 | }.Build() 558 | File_brpc_meta_proto = out.File 559 | file_brpc_meta_proto_rawDesc = nil 560 | file_brpc_meta_proto_goTypes = nil 561 | file_brpc_meta_proto_depIdxs = nil 562 | } 563 | --------------------------------------------------------------------------------