├── .gitignore ├── README.md ├── benchmark ├── client │ ├── client.go │ └── hellowordl.pb.go └── server │ ├── hellowordl.pb.go │ ├── pprof.go │ ├── server.go │ └── service.go ├── client ├── calloptions.go ├── client.go └── options.go ├── codec ├── protocol.go ├── protocol_test.go └── serialize │ ├── json.go │ ├── msgpack.go │ ├── proto.go │ └── serialize.go ├── discov ├── README.md ├── discovery.go ├── etcd │ ├── etcd_register.go │ ├── etcd_register_test.go │ └── option.go └── service.go ├── err ├── code.go ├── err.pb.go └── err.proto ├── examples ├── helloworld │ ├── client │ │ └── client.go │ └── server │ │ ├── server.go │ │ └── service.go ├── interceptor │ ├── timeout │ │ ├── client │ │ │ └── client.go │ │ └── server │ │ │ ├── server.go │ │ │ └── service.go │ └── trace │ │ ├── client │ │ └── client.go │ │ └── server │ │ ├── server.go │ │ └── service.go ├── metadata │ ├── client │ │ └── client.go │ └── server │ │ ├── server.go │ │ └── service.go ├── msgpack │ ├── client │ │ └── client.go │ └── server │ │ ├── server.go │ │ └── service.go └── proto │ ├── client │ ├── client.go │ └── hellowordl.pb.go │ └── server │ ├── hellowordl.pb.go │ ├── hellowordl.proto │ ├── server.go │ └── service.go ├── go.mod ├── go.sum ├── imgs ├── img.png └── img_1.png ├── interceptor ├── clientinterceptor │ ├── timeoutinterceptor.go │ ├── traceinterceptor.go │ └── traceinterceptor_test.go ├── interceptor.go ├── interceptor_test.go └── serverinterceptor │ ├── timeoutinterceptor.go │ └── traceinterceptor.go ├── lb ├── loadbalance.go └── random.go ├── logger ├── log.go └── log_test.go ├── metadata ├── metadata.go ├── metadata.pb.go └── metadata.proto ├── pool ├── channel.go ├── channel_rpc_test.go ├── options.go └── pool.go ├── server ├── options.go ├── server.go └── server_test.go ├── trace ├── agent.go ├── attr.go ├── trace.go └── util.go ├── transport ├── tcp.go ├── transport.go └── udp.go └── util ├── net.go └── slice.go /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # srpc 2 | srpc只是一个玩具级的RPC框架,未经线上实际检验,请大家不要轻易使用(有问题概不负责哦), 3 | but,srpc是一个学习RPC框架的“好轮子”(王婆卖瓜自卖自夸,hhh),因为它足够简单,麻雀虽小五脏俱全。 4 | 5 | ## 服务注册&服务发现(etcd、zk、nacos、consul) 6 | 服务发现&服务注册在微服务框架中起着承上启下的作用,是微服务框架中十分重要的一环, 7 | 如果在微服务体系当中没有服务发现&服务注册,那么就像汽车没有方向盘,无法掌控前进的方向。 8 | 9 | ![](imgs/img_1.png) 10 | 11 | 在实现服务发现和服务注册当中我们通常会选择以下组件来实现 12 | - [X] ETCD 13 | - [ ] ZK 14 | - [ ] nacos 15 | - [ ] consul 16 | 17 | 因为srpc框架是Go语言实现的轮子,所以我们暂且先选择ETCD(Go语言实现)来实现服务发发现,虽然我个人认为ETCD并不是 18 | 实现服务发现&服务注册最优的组件(AP组件可能会好点),如果有人想实现其它组件可以提PR。推荐一篇 19 | 各组件对比的文章[https://mp.weixin.qq.com/s/MGmbAz41zJGXwyPrmEeRBw](https://mp.weixin.qq.com/s/MGmbAz41zJGXwyPrmEeRBw) 20 | 21 | ## 传输协议(http、http2、tcp、quic) 22 | - [ ] http 23 | - [ ] http2 24 | - [X] tcp 25 | - [ ] quic 26 | - [X] udp 27 | ## 数据序列化(json、pb、msgpack) 28 | - [X] json 29 | - [X] pb 30 | - [X] msgpack 31 | ## 数据压缩 32 | 33 | ## 服务治理 34 | ### 数据可观测性(logging、metric、trace) 35 | - [X] logging 36 | - [ ] metric 37 | - [X] trace 38 | ### 熔断 39 | - [ ] 熔断 40 | ### 限流(滑动窗口、漏斗、令牌桶) 41 | - [ ] 滑动窗口 42 | - [ ] 漏斗 43 | - [ ] 令牌桶 44 | ### 负载均衡 45 | - [X] rr 46 | - [ ] wrr 47 | - [ ] p2c 48 | - [X] random 49 | ### 重试 50 | - [ ] Failfast 51 | - [ ] Failover 52 | - [ ] Failtry 53 | - [ ] Failbackup 54 | ### 超时控制 55 | - [X] 级联超时控制 56 | 57 | ## 服务端 58 | 59 | ## 客户端 60 | 61 | ## 使用 62 | 63 | ### server 64 | 65 | 66 | server.go 67 | 68 | ```go 69 | package main 70 | 71 | import ( 72 | "context" 73 | "errors" 74 | "fmt" 75 | 76 | "github.com/wsx864321/srpc/discov/etcd" 77 | "github.com/wsx864321/srpc/server" 78 | ) 79 | 80 | 81 | type HelloWorld struct { 82 | } 83 | 84 | type HelloWorldReq struct { 85 | Name string `json:"name"` 86 | } 87 | 88 | type HelloWorldResp struct { 89 | Msg string `json:"msg"` 90 | } 91 | 92 | func (h *HelloWorld) SayHello(ctx context.Context, req *HelloWorldReq) (*HelloWorldResp, error) { 93 | return &HelloWorldResp{ 94 | Msg: fmt.Sprintf("%s say hello", req.Name), 95 | }, nil 96 | } 97 | 98 | func main() { 99 | s := server.NewServer(server.WithDiscovery(etcd.NewETCDRegister(etcd.WithEndpoints([]string{"127.0.0.1:2371"})))) 100 | s.RegisterService("helloworld", &HelloWorld{}) 101 | s.Start() 102 | } 103 | 104 | ``` 105 | 106 | client.go 107 | 108 | ```go 109 | package main 110 | 111 | import ( 112 | "context" 113 | "fmt" 114 | "time" 115 | 116 | "github.com/wsx864321/srpc/client" 117 | "github.com/wsx864321/srpc/discov/etcd" 118 | ) 119 | 120 | type HelloWorldReq struct { 121 | Name string `json:"name"` 122 | } 123 | 124 | type HelloWorldResp struct { 125 | Msg string `json:"msg"` 126 | } 127 | 128 | func main() { 129 | req := &HelloWorldReq{ 130 | Name: "wsx", 131 | } 132 | var resp HelloWorldResp 133 | ctx, _ := context.WithTimeout(context.TODO(), 2*time.Second) 134 | cli := client.NewClient(client.WithServiceName("helloworld"), client.WithDiscovery(etcd.NewETCDRegister(etcd.WithEndpoints([]string{"127.0.0.1:2371"})))) 135 | err := cli.Call(ctx, "SayHello", req, &resp) 136 | fmt.Println(resp, err) 137 | } 138 | ``` 139 | 140 | ## TODOLIST 141 | - 实现客户端异步请求方式 142 | - 实现基于proto的插件来生成客户端和服务端的脚手架 143 | - 实现Unix Domain Socket的通信方式 144 | - 实现P2C的负载均衡方式 145 | - 实现长连接subset子集划分 146 | - 实现《The Tail at Scale》论文上优化长尾耗时的一些方法?? 147 | - 优化现有代码风格,增强健壮性 148 | - ......... 149 | 150 | ## END 151 | 如在使用过程当中有任何疑问和问题都可以提issue,如对本框架有任何建议也请老板们不吝赐教 -------------------------------------------------------------------------------- /benchmark/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "sync" 8 | "sync/atomic" 9 | "time" 10 | 11 | "github.com/wsx864321/srpc/interceptor" 12 | "github.com/wsx864321/srpc/interceptor/clientinterceptor" 13 | 14 | "github.com/wsx864321/srpc/pool" 15 | 16 | "github.com/wsx864321/srpc/client" 17 | "github.com/wsx864321/srpc/discov/etcd" 18 | ) 19 | 20 | const cliCount = 100 21 | 22 | var concurrency = flag.Int64("concurrency", 100, "concurrency") 23 | var total = flag.Int64("total", 1000000, "total requests") 24 | 25 | type Counter struct { 26 | Succ int64 // 成功量 27 | Fail int64 // 失败量 28 | Total int64 // 总量 29 | Concurrency int64 // 并发量 30 | Cost int64 // 总耗时 ms 31 | } 32 | 33 | func main() { 34 | flag.Parse() 35 | benchmark(*total, *concurrency) 36 | } 37 | 38 | func benchmark(total int64, concurrency int64) { 39 | perClientReqs := int(total / concurrency) 40 | 41 | counter := &Counter{ 42 | Total: int64(perClientReqs) * concurrency, 43 | Concurrency: concurrency, 44 | Fail: 0, 45 | } 46 | req := &HelloWorldReq{ 47 | Name: "wsx", 48 | } 49 | clis := make([]*client.Client, concurrency) 50 | for i := 0; i < int(concurrency); i++ { 51 | clis[i] = client.NewClient( 52 | client.WithServiceName("helloworld"), 53 | client.WithDiscovery(etcd.NewETCDRegister(etcd.WithEndpoints([]string{"127.0.0.1:2371"}))), 54 | client.WithPool(pool.NewPool(pool.WithInitialCap(10), pool.WithMaxCap(200))), 55 | client.WithReadTimeout(5*time.Second), 56 | client.WithWriteTimeout(5*time.Second), 57 | client.WithInterceptors([]interceptor.ClientInterceptor{clientinterceptor.ClientTimeoutInterceptor()}...), 58 | ) 59 | } 60 | 61 | defer func() { 62 | for _, c := range clis { 63 | c.Close() 64 | } 65 | }() 66 | 67 | startTime := time.Now().UnixNano() 68 | wg := sync.WaitGroup{} 69 | wg.Add(int(concurrency)) 70 | for i := 0; i < int(concurrency); i++ { 71 | go func(cc int) { 72 | defer wg.Done() 73 | for j := 0; j < perClientReqs; j++ { 74 | var resp HelloWorldResp 75 | ctx, _ := context.WithTimeout(context.TODO(), 40*time.Second) 76 | err := clis[cc].Call(ctx, "SayHello", req, &resp) 77 | if err != nil { 78 | atomic.AddInt64(&counter.Fail, 1) 79 | } 80 | } 81 | 82 | }(i) 83 | 84 | } 85 | 86 | wg.Wait() 87 | counter.Succ = total - counter.Fail 88 | counter.Cost = (time.Now().UnixNano() - startTime) / 1000000 89 | 90 | fmt.Printf("took %d ms for %d requests \n", counter.Cost, counter.Total) 91 | fmt.Printf("sent requests : %d\n", counter.Total) 92 | fmt.Printf("received requests : %d\n", atomic.LoadInt64(&counter.Succ)+atomic.LoadInt64(&counter.Fail)) 93 | fmt.Printf("received requests succ : %d\n", atomic.LoadInt64(&counter.Succ)) 94 | fmt.Printf("received requests fail : %d\n", atomic.LoadInt64(&counter.Fail)) 95 | fmt.Printf("throughput (TPS) : %d\n", total*1000/counter.Cost) 96 | } 97 | -------------------------------------------------------------------------------- /benchmark/client/hellowordl.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go v1.28.1 4 | // protoc v3.21.12 5 | // source: hellowordl.proto 6 | 7 | package main 8 | 9 | import ( 10 | reflect "reflect" 11 | sync "sync" 12 | 13 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 14 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 15 | ) 16 | 17 | const ( 18 | // Verify that this generated code is sufficiently up-to-date. 19 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 20 | // Verify that runtime/protoimpl is sufficiently up-to-date. 21 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 22 | ) 23 | 24 | type HelloWorldReq struct { 25 | state protoimpl.MessageState 26 | sizeCache protoimpl.SizeCache 27 | unknownFields protoimpl.UnknownFields 28 | 29 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 30 | } 31 | 32 | func (x *HelloWorldReq) Reset() { 33 | *x = HelloWorldReq{} 34 | if protoimpl.UnsafeEnabled { 35 | mi := &file_hellowordl_proto_msgTypes[0] 36 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 37 | ms.StoreMessageInfo(mi) 38 | } 39 | } 40 | 41 | func (x *HelloWorldReq) String() string { 42 | return protoimpl.X.MessageStringOf(x) 43 | } 44 | 45 | func (*HelloWorldReq) ProtoMessage() {} 46 | 47 | func (x *HelloWorldReq) ProtoReflect() protoreflect.Message { 48 | mi := &file_hellowordl_proto_msgTypes[0] 49 | if protoimpl.UnsafeEnabled && x != nil { 50 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 51 | if ms.LoadMessageInfo() == nil { 52 | ms.StoreMessageInfo(mi) 53 | } 54 | return ms 55 | } 56 | return mi.MessageOf(x) 57 | } 58 | 59 | // Deprecated: Use HelloWorldReq.ProtoReflect.Descriptor instead. 60 | func (*HelloWorldReq) Descriptor() ([]byte, []int) { 61 | return file_hellowordl_proto_rawDescGZIP(), []int{0} 62 | } 63 | 64 | func (x *HelloWorldReq) GetName() string { 65 | if x != nil { 66 | return x.Name 67 | } 68 | return "" 69 | } 70 | 71 | type HelloWorldResp struct { 72 | state protoimpl.MessageState 73 | sizeCache protoimpl.SizeCache 74 | unknownFields protoimpl.UnknownFields 75 | 76 | Msg string `protobuf:"bytes,1,opt,name=Msg,proto3" json:"Msg,omitempty"` 77 | } 78 | 79 | func (x *HelloWorldResp) Reset() { 80 | *x = HelloWorldResp{} 81 | if protoimpl.UnsafeEnabled { 82 | mi := &file_hellowordl_proto_msgTypes[1] 83 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 84 | ms.StoreMessageInfo(mi) 85 | } 86 | } 87 | 88 | func (x *HelloWorldResp) String() string { 89 | return protoimpl.X.MessageStringOf(x) 90 | } 91 | 92 | func (*HelloWorldResp) ProtoMessage() {} 93 | 94 | func (x *HelloWorldResp) ProtoReflect() protoreflect.Message { 95 | mi := &file_hellowordl_proto_msgTypes[1] 96 | if protoimpl.UnsafeEnabled && x != nil { 97 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 98 | if ms.LoadMessageInfo() == nil { 99 | ms.StoreMessageInfo(mi) 100 | } 101 | return ms 102 | } 103 | return mi.MessageOf(x) 104 | } 105 | 106 | // Deprecated: Use HelloWorldResp.ProtoReflect.Descriptor instead. 107 | func (*HelloWorldResp) Descriptor() ([]byte, []int) { 108 | return file_hellowordl_proto_rawDescGZIP(), []int{1} 109 | } 110 | 111 | func (x *HelloWorldResp) GetMsg() string { 112 | if x != nil { 113 | return x.Msg 114 | } 115 | return "" 116 | } 117 | 118 | var File_hellowordl_proto protoreflect.FileDescriptor 119 | 120 | var file_hellowordl_proto_rawDesc = []byte{ 121 | 0x0a, 0x10, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x64, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 122 | 0x74, 0x6f, 0x22, 0x23, 0x0a, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x57, 0x6f, 0x72, 0x6c, 0x64, 123 | 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 124 | 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x22, 0x0a, 0x0e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 125 | 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x4d, 0x73, 0x67, 126 | 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x4d, 0x73, 0x67, 0x42, 0x09, 0x5a, 0x07, 0x2e, 127 | 0x2f, 0x3b, 0x6d, 0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 128 | } 129 | 130 | var ( 131 | file_hellowordl_proto_rawDescOnce sync.Once 132 | file_hellowordl_proto_rawDescData = file_hellowordl_proto_rawDesc 133 | ) 134 | 135 | func file_hellowordl_proto_rawDescGZIP() []byte { 136 | file_hellowordl_proto_rawDescOnce.Do(func() { 137 | file_hellowordl_proto_rawDescData = protoimpl.X.CompressGZIP(file_hellowordl_proto_rawDescData) 138 | }) 139 | return file_hellowordl_proto_rawDescData 140 | } 141 | 142 | var file_hellowordl_proto_msgTypes = make([]protoimpl.MessageInfo, 2) 143 | var file_hellowordl_proto_goTypes = []interface{}{ 144 | (*HelloWorldReq)(nil), // 0: HelloWorldReq 145 | (*HelloWorldResp)(nil), // 1: HelloWorldResp 146 | } 147 | var file_hellowordl_proto_depIdxs = []int32{ 148 | 0, // [0:0] is the sub-list for method output_type 149 | 0, // [0:0] is the sub-list for method input_type 150 | 0, // [0:0] is the sub-list for extension type_name 151 | 0, // [0:0] is the sub-list for extension extendee 152 | 0, // [0:0] is the sub-list for field type_name 153 | } 154 | 155 | func init() { file_hellowordl_proto_init() } 156 | func file_hellowordl_proto_init() { 157 | if File_hellowordl_proto != nil { 158 | return 159 | } 160 | if !protoimpl.UnsafeEnabled { 161 | file_hellowordl_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 162 | switch v := v.(*HelloWorldReq); i { 163 | case 0: 164 | return &v.state 165 | case 1: 166 | return &v.sizeCache 167 | case 2: 168 | return &v.unknownFields 169 | default: 170 | return nil 171 | } 172 | } 173 | file_hellowordl_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { 174 | switch v := v.(*HelloWorldResp); i { 175 | case 0: 176 | return &v.state 177 | case 1: 178 | return &v.sizeCache 179 | case 2: 180 | return &v.unknownFields 181 | default: 182 | return nil 183 | } 184 | } 185 | } 186 | type x struct{} 187 | out := protoimpl.TypeBuilder{ 188 | File: protoimpl.DescBuilder{ 189 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 190 | RawDescriptor: file_hellowordl_proto_rawDesc, 191 | NumEnums: 0, 192 | NumMessages: 2, 193 | NumExtensions: 0, 194 | NumServices: 0, 195 | }, 196 | GoTypes: file_hellowordl_proto_goTypes, 197 | DependencyIndexes: file_hellowordl_proto_depIdxs, 198 | MessageInfos: file_hellowordl_proto_msgTypes, 199 | }.Build() 200 | File_hellowordl_proto = out.File 201 | file_hellowordl_proto_rawDesc = nil 202 | file_hellowordl_proto_goTypes = nil 203 | file_hellowordl_proto_depIdxs = nil 204 | } 205 | -------------------------------------------------------------------------------- /benchmark/server/hellowordl.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go v1.28.1 4 | // protoc v3.21.12 5 | // source: hellowordl.proto 6 | 7 | package main 8 | 9 | import ( 10 | reflect "reflect" 11 | sync "sync" 12 | 13 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 14 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 15 | ) 16 | 17 | const ( 18 | // Verify that this generated code is sufficiently up-to-date. 19 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 20 | // Verify that runtime/protoimpl is sufficiently up-to-date. 21 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 22 | ) 23 | 24 | type HelloWorldReq struct { 25 | state protoimpl.MessageState 26 | sizeCache protoimpl.SizeCache 27 | unknownFields protoimpl.UnknownFields 28 | 29 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 30 | } 31 | 32 | func (x *HelloWorldReq) Reset() { 33 | *x = HelloWorldReq{} 34 | if protoimpl.UnsafeEnabled { 35 | mi := &file_hellowordl_proto_msgTypes[0] 36 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 37 | ms.StoreMessageInfo(mi) 38 | } 39 | } 40 | 41 | func (x *HelloWorldReq) String() string { 42 | return protoimpl.X.MessageStringOf(x) 43 | } 44 | 45 | func (*HelloWorldReq) ProtoMessage() {} 46 | 47 | func (x *HelloWorldReq) ProtoReflect() protoreflect.Message { 48 | mi := &file_hellowordl_proto_msgTypes[0] 49 | if protoimpl.UnsafeEnabled && x != nil { 50 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 51 | if ms.LoadMessageInfo() == nil { 52 | ms.StoreMessageInfo(mi) 53 | } 54 | return ms 55 | } 56 | return mi.MessageOf(x) 57 | } 58 | 59 | // Deprecated: Use HelloWorldReq.ProtoReflect.Descriptor instead. 60 | func (*HelloWorldReq) Descriptor() ([]byte, []int) { 61 | return file_hellowordl_proto_rawDescGZIP(), []int{0} 62 | } 63 | 64 | func (x *HelloWorldReq) GetName() string { 65 | if x != nil { 66 | return x.Name 67 | } 68 | return "" 69 | } 70 | 71 | type HelloWorldResp struct { 72 | state protoimpl.MessageState 73 | sizeCache protoimpl.SizeCache 74 | unknownFields protoimpl.UnknownFields 75 | 76 | Msg string `protobuf:"bytes,1,opt,name=Msg,proto3" json:"Msg,omitempty"` 77 | } 78 | 79 | func (x *HelloWorldResp) Reset() { 80 | *x = HelloWorldResp{} 81 | if protoimpl.UnsafeEnabled { 82 | mi := &file_hellowordl_proto_msgTypes[1] 83 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 84 | ms.StoreMessageInfo(mi) 85 | } 86 | } 87 | 88 | func (x *HelloWorldResp) String() string { 89 | return protoimpl.X.MessageStringOf(x) 90 | } 91 | 92 | func (*HelloWorldResp) ProtoMessage() {} 93 | 94 | func (x *HelloWorldResp) ProtoReflect() protoreflect.Message { 95 | mi := &file_hellowordl_proto_msgTypes[1] 96 | if protoimpl.UnsafeEnabled && x != nil { 97 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 98 | if ms.LoadMessageInfo() == nil { 99 | ms.StoreMessageInfo(mi) 100 | } 101 | return ms 102 | } 103 | return mi.MessageOf(x) 104 | } 105 | 106 | // Deprecated: Use HelloWorldResp.ProtoReflect.Descriptor instead. 107 | func (*HelloWorldResp) Descriptor() ([]byte, []int) { 108 | return file_hellowordl_proto_rawDescGZIP(), []int{1} 109 | } 110 | 111 | func (x *HelloWorldResp) GetMsg() string { 112 | if x != nil { 113 | return x.Msg 114 | } 115 | return "" 116 | } 117 | 118 | var File_hellowordl_proto protoreflect.FileDescriptor 119 | 120 | var file_hellowordl_proto_rawDesc = []byte{ 121 | 0x0a, 0x10, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x64, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 122 | 0x74, 0x6f, 0x22, 0x23, 0x0a, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x57, 0x6f, 0x72, 0x6c, 0x64, 123 | 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 124 | 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x22, 0x0a, 0x0e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 125 | 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x4d, 0x73, 0x67, 126 | 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x4d, 0x73, 0x67, 0x42, 0x09, 0x5a, 0x07, 0x2e, 127 | 0x2f, 0x3b, 0x6d, 0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 128 | } 129 | 130 | var ( 131 | file_hellowordl_proto_rawDescOnce sync.Once 132 | file_hellowordl_proto_rawDescData = file_hellowordl_proto_rawDesc 133 | ) 134 | 135 | func file_hellowordl_proto_rawDescGZIP() []byte { 136 | file_hellowordl_proto_rawDescOnce.Do(func() { 137 | file_hellowordl_proto_rawDescData = protoimpl.X.CompressGZIP(file_hellowordl_proto_rawDescData) 138 | }) 139 | return file_hellowordl_proto_rawDescData 140 | } 141 | 142 | var file_hellowordl_proto_msgTypes = make([]protoimpl.MessageInfo, 2) 143 | var file_hellowordl_proto_goTypes = []interface{}{ 144 | (*HelloWorldReq)(nil), // 0: HelloWorldReq 145 | (*HelloWorldResp)(nil), // 1: HelloWorldResp 146 | } 147 | var file_hellowordl_proto_depIdxs = []int32{ 148 | 0, // [0:0] is the sub-list for method output_type 149 | 0, // [0:0] is the sub-list for method input_type 150 | 0, // [0:0] is the sub-list for extension type_name 151 | 0, // [0:0] is the sub-list for extension extendee 152 | 0, // [0:0] is the sub-list for field type_name 153 | } 154 | 155 | func init() { file_hellowordl_proto_init() } 156 | func file_hellowordl_proto_init() { 157 | if File_hellowordl_proto != nil { 158 | return 159 | } 160 | if !protoimpl.UnsafeEnabled { 161 | file_hellowordl_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 162 | switch v := v.(*HelloWorldReq); i { 163 | case 0: 164 | return &v.state 165 | case 1: 166 | return &v.sizeCache 167 | case 2: 168 | return &v.unknownFields 169 | default: 170 | return nil 171 | } 172 | } 173 | file_hellowordl_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { 174 | switch v := v.(*HelloWorldResp); i { 175 | case 0: 176 | return &v.state 177 | case 1: 178 | return &v.sizeCache 179 | case 2: 180 | return &v.unknownFields 181 | default: 182 | return nil 183 | } 184 | } 185 | } 186 | type x struct{} 187 | out := protoimpl.TypeBuilder{ 188 | File: protoimpl.DescBuilder{ 189 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 190 | RawDescriptor: file_hellowordl_proto_rawDesc, 191 | NumEnums: 0, 192 | NumMessages: 2, 193 | NumExtensions: 0, 194 | NumServices: 0, 195 | }, 196 | GoTypes: file_hellowordl_proto_goTypes, 197 | DependencyIndexes: file_hellowordl_proto_depIdxs, 198 | MessageInfos: file_hellowordl_proto_msgTypes, 199 | }.Build() 200 | File_hellowordl_proto = out.File 201 | file_hellowordl_proto_rawDesc = nil 202 | file_hellowordl_proto_goTypes = nil 203 | file_hellowordl_proto_depIdxs = nil 204 | } 205 | -------------------------------------------------------------------------------- /benchmark/server/pprof.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | _ "net/http/pprof" 6 | ) 7 | 8 | func pprof() { 9 | go func() { 10 | http.ListenAndServe("0.0.0.0:8899", http.DefaultServeMux) 11 | }() 12 | } 13 | -------------------------------------------------------------------------------- /benchmark/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | _ "net/http/pprof" 5 | 6 | "github.com/wsx864321/srpc/codec/serialize" 7 | 8 | "github.com/wsx864321/srpc/discov/etcd" 9 | "github.com/wsx864321/srpc/server" 10 | ) 11 | 12 | func main() { 13 | pprof() 14 | 15 | s := server.NewServer( 16 | server.WithDiscovery(etcd.NewETCDRegister(etcd.WithEndpoints([]string{"127.0.0.1:2371"}))), 17 | server.WithSerialize(serialize.SerializeTypeProto), 18 | ) 19 | s.RegisterService("helloworld", &HelloWorld{}) 20 | s.Start() 21 | } 22 | -------------------------------------------------------------------------------- /benchmark/server/service.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | ) 7 | 8 | type HelloWorld struct { 9 | } 10 | 11 | func (h *HelloWorld) SayHello(ctx context.Context, req *HelloWorldReq) (*HelloWorldResp, error) { 12 | return &HelloWorldResp{ 13 | Msg: fmt.Sprintf("%s say hello", req.Name), 14 | }, nil 15 | } 16 | -------------------------------------------------------------------------------- /client/calloptions.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import "time" 4 | 5 | type FailMode int 6 | 7 | const ( 8 | FailOver FailMode = iota // 尝试另外一个节点 9 | FailFast // 立即失败 10 | FailTry // 对当前节点进行重试,直到超过重试次数 11 | FailBackup // 对冲请求(当前对冲请求需要设置latency,之后会仿照prometheus的Histograms指标类型进行分位的收集,业务只需要传递类似P95 P99这样的配置即可) 12 | ) 13 | 14 | type callOptions struct { 15 | target string // 不采用服务发现的方式直接进行请求 16 | failMode FailMode // 失败模式 17 | retries int // retry次数 18 | backupLatency time.Duration // 使用备选节点时候的延时 19 | } 20 | 21 | type CallOption func(opt *callOptions) 22 | 23 | // WithTarget 设置target 24 | func WithTarget(addr string) CallOption { 25 | return func(opt *callOptions) { 26 | opt.target = addr 27 | } 28 | } 29 | 30 | // WithFailMode 设置失败模式 31 | func WithFailMode(mode FailMode) CallOption { 32 | return func(opt *callOptions) { 33 | opt.failMode = mode 34 | } 35 | } 36 | 37 | // WithRetries 设置FailTry模式下的retry次数 38 | func WithRetries(retries int) CallOption { 39 | return func(opt *callOptions) { 40 | opt.retries = retries 41 | } 42 | } 43 | 44 | // WithBackupLatency 设置对冲请求的延时时间限制 45 | func WithBackupLatency(backupLatency time.Duration) CallOption { 46 | return func(opt *callOptions) { 47 | opt.backupLatency = backupLatency 48 | } 49 | } 50 | 51 | // NewCallOptions ... 52 | func NewCallOptions(opts ...CallOption) *callOptions { 53 | var o callOptions 54 | for _, opt := range opts { 55 | opt(&o) 56 | } 57 | 58 | return &o 59 | } 60 | -------------------------------------------------------------------------------- /client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/wsx864321/srpc/metadata" 8 | 9 | "net" 10 | "time" 11 | 12 | srpcerr "github.com/wsx864321/srpc/err" 13 | 14 | "github.com/wsx864321/srpc/codec" 15 | "github.com/wsx864321/srpc/codec/serialize" 16 | "github.com/wsx864321/srpc/interceptor" 17 | "github.com/wsx864321/srpc/lb" 18 | "github.com/wsx864321/srpc/util" 19 | ) 20 | 21 | type Client struct { 22 | opts *Options 23 | 24 | codec *codec.Codec 25 | } 26 | 27 | // NewClient 生成client对象 28 | func NewClient(opts ...Option) *Client { 29 | client := &Client{ 30 | opts: NewOptions(opts...), 31 | codec: codec.NewCodec(), 32 | } 33 | 34 | return client 35 | } 36 | 37 | func (c *Client) Call(ctx context.Context, methodName string, req, resp interface{}, opts ...CallOption) error { 38 | // 1.获取服务地址 39 | service, err := c.opts.dis.GetService(ctx, c.opts.serviceName) 40 | if err != nil { 41 | return err 42 | } 43 | 44 | // 2.进行负载均衡策略,选择node 45 | endpoint, err := lb.GetLB(c.opts.lbName).Pick(service) 46 | if err != nil { 47 | return err 48 | } 49 | 50 | // 3.从连接池获取链接 51 | conn, err := c.opts.pool.Get(ctx, endpoint.Network, endpoint.GetAddr()) 52 | if err != nil { 53 | return err 54 | } 55 | defer c.opts.pool.Put(endpoint.Network, endpoint.GetAddr(), conn) 56 | 57 | // 4.组装invoke(数据序列化+数据编码+发送数据+接手数据) 58 | fullMethod := fmt.Sprintf("%s/%s", c.opts.serviceName, methodName) 59 | target := endpoint.GetAddr() 60 | invoker := func(ctx context.Context, req, resp interface{}) error { 61 | // 4.1 数据序列化 62 | raw, err := serialize.GetSerialize(serialize.SerializeType(endpoint.Serialize)).Marshal(req) 63 | if err != nil { 64 | return err 65 | } 66 | // 4.2 获取metadata(trace、级联超时、用户其它自定义的数据等等) 67 | metaData := metadata.ExtractClientMetadata(ctx) 68 | peerAddr := conn.LocalAddr().String() 69 | metaData[metadata.SRPCPeerAddr] = peerAddr 70 | metaData[metadata.SPRCFullMethod] = fullMethod 71 | metaDataRaw, err := serialize.GetSerialize(serialize.SerializeType(endpoint.Serialize)).Marshal(&metadata.Metadata{ 72 | Data: metaData, 73 | }) 74 | if err != nil { 75 | return err 76 | } 77 | // 4.3 执行编码 78 | request, err := codec.NewCodec().Encode( 79 | codec.GeneralMsgType, 80 | codec.CompressTypeNot, 81 | uint64(time.Now().Unix()), 82 | []byte(c.opts.serviceName), 83 | []byte(methodName), 84 | metaDataRaw, 85 | raw, 86 | ) 87 | if err != nil { 88 | return err 89 | } 90 | 91 | // 4.4 发送请求数据 92 | if c.opts.writeTimeout > 0 { 93 | if err = conn.SetWriteDeadline(time.Now().Add(c.opts.writeTimeout)); err != nil { 94 | return err 95 | } 96 | } 97 | 98 | if err = util.Write(conn, request); err != nil { 99 | return err 100 | } 101 | 102 | // 4.5 接受数据 103 | msg, err := c.extractMessage(conn) 104 | if err != nil { 105 | return err 106 | } 107 | 108 | var errResp srpcerr.Error 109 | if err = serialize.GetSerialize(serialize.SerializeType(endpoint.Serialize)).Unmarshal(msg.Payload, &errResp); err != nil { 110 | return err 111 | } 112 | 113 | if errResp.Code != srpcerr.Ok { 114 | return &errResp 115 | } 116 | 117 | if err = serialize.GetSerialize(serialize.SerializeType(endpoint.Serialize)).Unmarshal(errResp.Data, resp); err != nil { 118 | return err 119 | } 120 | 121 | return nil 122 | } 123 | 124 | // 5.执行中间件以及invoker函数 125 | return interceptor.ClientIntercept(ctx, fullMethod, target, req, resp, c.opts.interceptors, invoker) 126 | } 127 | 128 | // extractMessage 提取message内容 129 | func (c *Client) extractMessage(conn net.Conn) (*codec.Message, error) { 130 | // 1.设置读取超时时间 131 | if c.opts.readTimeout > 0 { 132 | if err := conn.SetReadDeadline(time.Now().Add(c.opts.readTimeout)); err != nil { 133 | return nil, err 134 | } 135 | } 136 | 137 | // 2.读取头部内容 138 | headerData := make([]byte, c.codec.GetHeaderLength()) 139 | if err := util.ReadFixData(conn, headerData); err != nil { 140 | return nil, err 141 | } 142 | 143 | header, err := c.codec.DecodeHeader(headerData) 144 | if err != nil { 145 | return nil, err 146 | } 147 | 148 | // 3.读取message内容 149 | body := make([]byte, c.codec.GetBodyLength(header)) 150 | if err = util.ReadFixData(conn, body); err != nil { 151 | return nil, err 152 | } 153 | 154 | return c.codec.DecodeBody(header, body) 155 | } 156 | 157 | func (c *Client) Close() { 158 | c.opts.pool.CloseAll() 159 | } 160 | -------------------------------------------------------------------------------- /client/options.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/wsx864321/srpc/discov" 5 | "github.com/wsx864321/srpc/discov/etcd" 6 | "github.com/wsx864321/srpc/interceptor" 7 | "github.com/wsx864321/srpc/lb" 8 | "github.com/wsx864321/srpc/pool" 9 | "time" 10 | ) 11 | 12 | var defaultOptions = &Options{ 13 | writeTimeout: 500 * time.Millisecond, 14 | timeout: 5 * time.Second, 15 | readTimeout: 500 * time.Millisecond, 16 | lbName: lb.LoadBalanceRandom, 17 | dis: etcd.NewETCDRegister(), 18 | pool: pool.NewPool(), 19 | } 20 | 21 | type Options struct { 22 | serviceName string 23 | interceptors []interceptor.ClientInterceptor 24 | writeTimeout time.Duration 25 | timeout time.Duration 26 | readTimeout time.Duration 27 | lbName string 28 | dis discov.Discovery 29 | pool *pool.Pool 30 | } 31 | 32 | type Option func(o *Options) 33 | 34 | func WithServiceName(srvName string) Option { 35 | return func(o *Options) { 36 | o.serviceName = srvName 37 | } 38 | } 39 | 40 | func WithInterceptors(interceptor ...interceptor.ClientInterceptor) Option { 41 | return func(o *Options) { 42 | o.interceptors = interceptor 43 | } 44 | } 45 | 46 | func WithWriteTimeout(duration time.Duration) Option { 47 | return func(o *Options) { 48 | o.writeTimeout = duration 49 | } 50 | } 51 | 52 | func WithTimeout(duration time.Duration) Option { 53 | return func(o *Options) { 54 | o.timeout = duration 55 | } 56 | } 57 | 58 | func WithReadTimeout(duration time.Duration) Option { 59 | return func(o *Options) { 60 | o.readTimeout = duration 61 | } 62 | } 63 | 64 | func WithLoadBalance(name string) Option { 65 | return func(o *Options) { 66 | o.lbName = name 67 | } 68 | } 69 | 70 | func WithDiscovery(discovery discov.Discovery) Option { 71 | return func(o *Options) { 72 | o.dis = discovery 73 | } 74 | } 75 | 76 | func WithPool(pool *pool.Pool) Option { 77 | return func(o *Options) { 78 | o.pool = pool 79 | } 80 | } 81 | 82 | // NewOptions 初始化option 83 | func NewOptions(opts ...Option) *Options { 84 | opt := defaultOptions 85 | for _, fn := range opts { 86 | fn(opt) 87 | } 88 | 89 | return opt 90 | } 91 | -------------------------------------------------------------------------------- /codec/protocol.go: -------------------------------------------------------------------------------- 1 | package codec 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | ) 8 | 9 | const ( 10 | magicNumConst uint8 = 0x11 11 | versionConst uint8 = 0x1 12 | headerByteLength = 24 13 | 14 | GeneralMsgType uint8 = 0x0 15 | 16 | CompressTypeNot uint8 = 0x0 17 | ) 18 | 19 | /** 20 | 协议设计,TLV,|tag|length|value 21 | |MagicNum|Version|MsgType|CompressType|ServiceNameSize|ServiceMethodSize|MateSize|DataSize|Seq |ServiceName|ServiceMethod|MetaData|Payload 22 | |1byte |1byte |1byte |1byte |2byte |2byte |4byte |4byte |8byte |x bytes |x bytes |x bytes |x bytes 23 | */ 24 | 25 | // Message todo 修改成rpc调用四元素 caller callerFunc callee callFunc 26 | type Message struct { 27 | *Header 28 | ServiceName string 29 | ServiceMethod string 30 | MetaData []byte 31 | Payload []byte 32 | } 33 | 34 | // Header 设计上考虑了内存对齐 35 | type Header struct { 36 | MagicNum uint8 37 | Version uint8 // version 38 | MsgType uint8 // msg type e.g. : 0x0: general req, 0x1: heartbeat 39 | CompressType uint8 // compression or not : 0x0: not compression, 0x1: compression 40 | ServiceNameSize uint16 41 | ServiceMethodSize uint16 42 | MetaSize uint32 43 | PayloadSize uint32 44 | Seq uint64 // stream ID 45 | } 46 | 47 | type Codec struct { 48 | } 49 | 50 | func NewCodec() *Codec { 51 | return &Codec{} 52 | } 53 | 54 | // Encode ... 55 | func (c *Codec) Encode(msgType, compressType uint8, seq uint64, serviceName, serviceMethod, metaData, payload []byte) ([]byte, error) { 56 | buffer := bytes.NewBuffer(make([]byte, 0, headerByteLength+len(serviceName)+len(serviceMethod)+len(metaData)+len(payload))) 57 | if err := binary.Write(buffer, binary.BigEndian, magicNumConst); err != nil { 58 | return nil, err 59 | } 60 | 61 | if err := binary.Write(buffer, binary.BigEndian, versionConst); err != nil { 62 | return nil, err 63 | } 64 | 65 | if err := binary.Write(buffer, binary.BigEndian, msgType); err != nil { 66 | return nil, err 67 | } 68 | 69 | if err := binary.Write(buffer, binary.BigEndian, compressType); err != nil { 70 | return nil, err 71 | } 72 | 73 | if err := binary.Write(buffer, binary.BigEndian, uint16(len(serviceName))); err != nil { 74 | return nil, err 75 | } 76 | 77 | if err := binary.Write(buffer, binary.BigEndian, uint16(len(serviceMethod))); err != nil { 78 | return nil, err 79 | } 80 | 81 | if err := binary.Write(buffer, binary.BigEndian, uint32(len(metaData))); err != nil { 82 | return nil, err 83 | } 84 | 85 | if err := binary.Write(buffer, binary.BigEndian, uint32(len(payload))); err != nil { 86 | return nil, err 87 | } 88 | 89 | if err := binary.Write(buffer, binary.BigEndian, seq); err != nil { 90 | return nil, err 91 | } 92 | 93 | if _, err := buffer.Write(serviceName); err != nil { 94 | return nil, err 95 | } 96 | if _, err := buffer.Write(serviceMethod); err != nil { 97 | return nil, err 98 | } 99 | 100 | if _, err := buffer.Write(metaData); err != nil { 101 | return nil, err 102 | } 103 | 104 | if _, err := buffer.Write(payload); err != nil { 105 | return nil, err 106 | } 107 | 108 | return buffer.Bytes(), nil 109 | } 110 | 111 | func (c *Codec) Decode(data []byte) (*Message, error) { 112 | if len(data) < headerByteLength { 113 | return nil, errors.New("data length is at least 23") 114 | } 115 | 116 | var msg Message 117 | header, err := c.DecodeHeader(data[:headerByteLength]) 118 | if err != nil { 119 | return nil, err 120 | } 121 | 122 | serviceNameStartPos := headerByteLength 123 | msg.ServiceName = string(data[serviceNameStartPos : serviceNameStartPos+int(header.ServiceNameSize)]) 124 | 125 | serviceMethodStartPos := serviceNameStartPos + int(header.ServiceNameSize) 126 | msg.ServiceMethod = string(data[serviceMethodStartPos : serviceMethodStartPos+int(header.ServiceMethodSize)]) 127 | 128 | metaDataStartPos := serviceMethodStartPos + int(header.ServiceMethodSize) 129 | msg.MetaData = data[metaDataStartPos : metaDataStartPos+int(header.MetaSize)] 130 | 131 | payloadStartPos := metaDataStartPos + int(header.MetaSize) 132 | msg.Payload = data[payloadStartPos : payloadStartPos+int(header.PayloadSize)] 133 | 134 | msg.Header = header 135 | 136 | return &msg, nil 137 | } 138 | 139 | func (c *Codec) DecodeBody(header *Header, data []byte) (*Message, error) { 140 | var msg Message 141 | serviceNameStartPos := 0 142 | msg.ServiceName = string(data[serviceNameStartPos : serviceNameStartPos+int(header.ServiceNameSize)]) 143 | 144 | serviceMethodStartPos := serviceNameStartPos + int(header.ServiceNameSize) 145 | msg.ServiceMethod = string(data[serviceMethodStartPos : serviceMethodStartPos+int(header.ServiceMethodSize)]) 146 | 147 | metaDataStartPos := serviceMethodStartPos + int(header.ServiceMethodSize) 148 | msg.MetaData = data[metaDataStartPos : metaDataStartPos+int(header.MetaSize)] 149 | 150 | payloadStartPos := metaDataStartPos + int(header.MetaSize) 151 | msg.Payload = data[payloadStartPos : payloadStartPos+int(header.PayloadSize)] 152 | 153 | msg.Header = header 154 | 155 | return &msg, nil 156 | } 157 | 158 | func (c *Codec) DecodeHeader(data []byte) (*Header, error) { 159 | var ( 160 | magicNum uint8 161 | version uint8 162 | msgType uint8 163 | compressType uint8 164 | serviceNameSize uint16 165 | serviceMethodSize uint16 166 | mateSize uint32 167 | dataSize uint32 168 | seq uint64 169 | ) 170 | buffer := bytes.NewBuffer(data) 171 | 172 | if err := binary.Read(buffer, binary.BigEndian, &magicNum); err != nil { 173 | return nil, err 174 | } 175 | 176 | if magicNum != magicNumConst { 177 | return nil, errors.New("invalid data") 178 | } 179 | 180 | if err := binary.Read(buffer, binary.BigEndian, &version); err != nil { 181 | return nil, err 182 | } 183 | 184 | if err := binary.Read(buffer, binary.BigEndian, &msgType); err != nil { 185 | return nil, err 186 | } 187 | 188 | if err := binary.Read(buffer, binary.BigEndian, &compressType); err != nil { 189 | return nil, err 190 | } 191 | 192 | if err := binary.Read(buffer, binary.BigEndian, &serviceNameSize); err != nil { 193 | return nil, err 194 | } 195 | 196 | if err := binary.Read(buffer, binary.BigEndian, &serviceMethodSize); err != nil { 197 | return nil, err 198 | } 199 | 200 | if err := binary.Read(buffer, binary.BigEndian, &mateSize); err != nil { 201 | return nil, err 202 | } 203 | 204 | if err := binary.Read(buffer, binary.BigEndian, &dataSize); err != nil { 205 | return nil, err 206 | } 207 | 208 | if err := binary.Read(buffer, binary.BigEndian, &seq); err != nil { 209 | return nil, err 210 | } 211 | 212 | return &Header{ 213 | magicNum, 214 | version, 215 | msgType, 216 | compressType, 217 | serviceNameSize, 218 | serviceMethodSize, 219 | mateSize, 220 | dataSize, 221 | seq, 222 | }, nil 223 | } 224 | 225 | func (c *Codec) GetHeaderLength() int { 226 | return headerByteLength 227 | } 228 | 229 | func (c *Codec) GetBodyLength(header *Header) int { 230 | return int(header.ServiceNameSize) + int(header.ServiceMethodSize) + int(header.MetaSize) + int(header.PayloadSize) 231 | } 232 | 233 | type Response struct { 234 | Code int32 `json:"code"` 235 | Msg string `json:"msg"` 236 | Data interface{} `json:"data"` 237 | } 238 | -------------------------------------------------------------------------------- /codec/protocol_test.go: -------------------------------------------------------------------------------- 1 | package codec 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestCodec_Encode(t *testing.T) { 11 | c := NewCodec() 12 | resp, err := c.Encode(1, 1, 11111, []byte("testService"), []byte("index"), []byte("matedata"), []byte("payload")) 13 | assert.NoError(t, err) 14 | 15 | fmt.Println(resp) 16 | } 17 | 18 | func TestCodec_decodeHeader(t *testing.T) { 19 | h := Header{ 20 | 17, 1, 1, 1, 11, 5, 8, 7, 11111, 21 | } 22 | c := NewCodec() 23 | req, _ := c.Encode(1, 1, 11111, []byte("testService"), []byte("index"), []byte("matedata"), []byte("payload")) 24 | 25 | resp, err := c.DecodeHeader(req[:headerByteLength]) 26 | 27 | assert.NoError(t, err) 28 | 29 | assert.Equal(t, h, *resp) 30 | } 31 | 32 | func TestCodec_Decode(t *testing.T) { 33 | c := NewCodec() 34 | req, _ := c.Encode(1, 1, 11111, []byte("testService"), []byte("index"), []byte("matedata"), []byte("payload")) 35 | 36 | resp, err := c.Decode(req) 37 | 38 | assert.NoError(t, err) 39 | 40 | assert.Equal(t, resp.ServiceName, "testService") 41 | assert.Equal(t, resp.ServiceMethod, "index") 42 | assert.Equal(t, resp.MetaData, []byte("matedata")) 43 | assert.Equal(t, resp.Payload, []byte("payload")) 44 | } 45 | -------------------------------------------------------------------------------- /codec/serialize/json.go: -------------------------------------------------------------------------------- 1 | package serialize 2 | 3 | import "encoding/json" 4 | 5 | type JsonSerialize struct { 6 | } 7 | 8 | func NewJsonSerialize() Serialize { 9 | return &JsonSerialize{} 10 | } 11 | 12 | func (j *JsonSerialize) Marshal(v interface{}) ([]byte, error) { 13 | return json.Marshal(v) 14 | } 15 | 16 | func (j *JsonSerialize) Unmarshal(data []byte, v interface{}) error { 17 | return json.Unmarshal(data, v) 18 | } 19 | -------------------------------------------------------------------------------- /codec/serialize/msgpack.go: -------------------------------------------------------------------------------- 1 | package serialize 2 | 3 | import "github.com/vmihailenco/msgpack" 4 | 5 | type MsgpackSerialize struct { 6 | } 7 | 8 | func NewMsgpackSerialize() Serialize { 9 | return &MsgpackSerialize{} 10 | } 11 | 12 | func (m *MsgpackSerialize) Marshal(v interface{}) ([]byte, error) { 13 | return msgpack.Marshal(v) 14 | } 15 | 16 | func (m *MsgpackSerialize) Unmarshal(data []byte, v interface{}) error { 17 | return msgpack.Unmarshal(data, v) 18 | } 19 | -------------------------------------------------------------------------------- /codec/serialize/proto.go: -------------------------------------------------------------------------------- 1 | package serialize 2 | 3 | import ( 4 | "fmt" 5 | "github.com/golang/protobuf/proto" 6 | "reflect" 7 | ) 8 | 9 | type ProtoSerialize struct { 10 | } 11 | 12 | func NewProtoSerialize() Serialize { 13 | return &ProtoSerialize{} 14 | } 15 | 16 | func (p *ProtoSerialize) Marshal(v interface{}) ([]byte, error) { 17 | message, ok := v.(proto.Message) 18 | if !ok { 19 | return nil, fmt.Errorf("v is not implement proto.Message,v type:%v", reflect.TypeOf(v)) 20 | } 21 | 22 | return proto.Marshal(message) 23 | } 24 | 25 | func (p *ProtoSerialize) Unmarshal(data []byte, v interface{}) error { 26 | message, ok := v.(proto.Message) 27 | if !ok { 28 | return fmt.Errorf("v is not implement proto.Message,v type:%v", reflect.TypeOf(v)) 29 | } 30 | 31 | return proto.Unmarshal(data, message) 32 | } 33 | -------------------------------------------------------------------------------- /codec/serialize/serialize.go: -------------------------------------------------------------------------------- 1 | package serialize 2 | 3 | type SerializeType string 4 | 5 | const ( 6 | SerializeTypeJson SerializeType = "json" 7 | SerializeTypeMsgpack = "msgpack" 8 | SerializeTypeProto = "proto" 9 | ) 10 | 11 | var serializeMgr = map[SerializeType]Serialize{ 12 | SerializeTypeJson: NewJsonSerialize(), 13 | SerializeTypeMsgpack: NewMsgpackSerialize(), 14 | SerializeTypeProto: NewProtoSerialize(), 15 | } 16 | 17 | type Serialize interface { 18 | Marshal(v interface{}) ([]byte, error) 19 | Unmarshal(data []byte, v interface{}) error 20 | } 21 | 22 | // RegisterSerialize 注册序列化方式,可以自定义序列化方式 23 | func RegisterSerialize(serializeType SerializeType, serialize Serialize) { 24 | serializeMgr[serializeType] = serialize 25 | } 26 | 27 | // GetSerialize 获取序列化方式 28 | func GetSerialize(serializeType SerializeType) Serialize { 29 | return serializeMgr[serializeType] 30 | } 31 | -------------------------------------------------------------------------------- /discov/README.md: -------------------------------------------------------------------------------- 1 | ## 服务发现&服务注册 2 | 3 | ![img.png](../imgs/img_1.png) -------------------------------------------------------------------------------- /discov/discovery.go: -------------------------------------------------------------------------------- 1 | package discov 2 | 3 | import "context" 4 | 5 | type Discovery interface { 6 | Name() string 7 | Register(ctx context.Context, service *Service) 8 | UnRegister(ctx context.Context, service *Service) 9 | GetService(ctx context.Context, name string) (*Service, error) 10 | } 11 | -------------------------------------------------------------------------------- /discov/etcd/etcd_register.go: -------------------------------------------------------------------------------- 1 | package etcd 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "strconv" 8 | "strings" 9 | "sync" 10 | "sync/atomic" 11 | "time" 12 | 13 | "github.com/wsx864321/srpc/discov" 14 | clientv3 "go.etcd.io/etcd/client/v3" 15 | ) 16 | 17 | const KeyPrefix = "/srpc/service/register/" 18 | 19 | // Register ... 20 | type Register struct { 21 | Options 22 | cli *clientv3.Client 23 | serviceRegisterCh chan *discov.Service 24 | serviceUnRegisterCh chan *discov.Service 25 | lock sync.Mutex 26 | downServices atomic.Value 27 | registerServices map[string]*registerService 28 | } 29 | 30 | type registerService struct { 31 | service *discov.Service 32 | leaseID clientv3.LeaseID 33 | isRegistered bool 34 | keepAliveCh <-chan *clientv3.LeaseKeepAliveResponse 35 | } 36 | 37 | // NewETCDRegister ... 38 | func NewETCDRegister(opts ...Option) discov.Discovery { 39 | opt := defaultOption 40 | for _, o := range opts { 41 | o(&opt) 42 | } 43 | 44 | r := &Register{ 45 | Options: opt, 46 | serviceRegisterCh: make(chan *discov.Service), 47 | serviceUnRegisterCh: make(chan *discov.Service), 48 | lock: sync.Mutex{}, 49 | downServices: atomic.Value{}, 50 | registerServices: make(map[string]*registerService), 51 | } 52 | 53 | if err := r.init(context.TODO()); err != nil { 54 | panic(err) 55 | } 56 | 57 | return r 58 | } 59 | 60 | // init 初始化 61 | func (r *Register) init(ctx context.Context) error { 62 | var err error 63 | r.cli, err = clientv3.New( 64 | clientv3.Config{ 65 | Endpoints: r.endpoints, 66 | DialTimeout: r.dialTimeout, 67 | }) 68 | 69 | if err != nil { 70 | return err 71 | } 72 | 73 | go r.run() 74 | 75 | return nil 76 | } 77 | 78 | func (r *Register) run() { 79 | for { 80 | select { 81 | case service := <-r.serviceRegisterCh: 82 | if _, ok := r.registerServices[service.Name]; ok { 83 | r.registerServices[service.Name].service.Endpoints = append(r.registerServices[service.Name].service.Endpoints, service.Endpoints...) 84 | r.registerServices[service.Name].isRegistered = false // 重新上报到etcd 85 | } else { 86 | r.registerServices[service.Name] = ®isterService{ 87 | service: service, 88 | isRegistered: false, 89 | } 90 | } 91 | case service := <-r.serviceUnRegisterCh: 92 | if _, ok := r.registerServices[service.Name]; !ok { 93 | r.logger.Errorf(context.TODO(), "UnRegisterService err, service %v was not registered", service.Name) 94 | continue 95 | } 96 | r.unRegisterService(context.TODO(), service) 97 | default: 98 | r.registerServiceOrKeepAlive(context.TODO()) 99 | time.Sleep(r.registerServiceOrKeepAliveInterval) 100 | } 101 | } 102 | } 103 | 104 | func (r *Register) registerServiceOrKeepAlive(ctx context.Context) { 105 | for _, service := range r.registerServices { 106 | if !service.isRegistered { 107 | r.registerService(ctx, service) 108 | r.registerServices[service.service.Name].isRegistered = true 109 | } else { 110 | r.KeepAlive(ctx, service) 111 | } 112 | } 113 | } 114 | 115 | func (r *Register) registerService(ctx context.Context, service *registerService) { 116 | leaseGrantResp, err := r.cli.Grant(ctx, r.keepAliveInterval) 117 | if err != nil { 118 | r.logger.Errorf(ctx, "register service grant,err:%v", err) 119 | return 120 | } 121 | service.leaseID = leaseGrantResp.ID 122 | 123 | for _, endpoint := range service.service.Endpoints { 124 | 125 | key := r.getEtcdRegisterKey(service.service.Name, endpoint.IP, endpoint.Port) 126 | raw, err := json.Marshal(endpoint) 127 | if err != nil { 128 | r.logger.Errorf(ctx, "register service err,err:%v, register data:%v", err, string(raw)) 129 | continue 130 | } 131 | 132 | _, err = r.cli.Put(ctx, key, string(raw), clientv3.WithLease(leaseGrantResp.ID)) 133 | if err != nil { 134 | r.logger.Errorf(ctx, "register service err,err:%v, register data:%v", err, string(raw)) 135 | continue 136 | } 137 | 138 | } 139 | 140 | keepAliveCh, err := r.cli.KeepAlive(ctx, leaseGrantResp.ID) 141 | if err != nil { 142 | r.logger.Errorf(ctx, "register service keepalive,err:%v", err) 143 | return 144 | } 145 | 146 | service.keepAliveCh = keepAliveCh 147 | service.isRegistered = true 148 | 149 | } 150 | 151 | func (r *Register) unRegisterService(ctx context.Context, service *discov.Service) { 152 | endpoints := make([]*discov.Endpoint, 0) 153 | for _, endpoint := range r.registerServices[service.Name].service.Endpoints { 154 | var isRemove bool 155 | for _, unRegisterEndpoint := range service.Endpoints { 156 | if endpoint.IP == unRegisterEndpoint.IP && endpoint.Port == unRegisterEndpoint.Port { 157 | _, err := r.cli.Delete(context.TODO(), r.getEtcdRegisterKey(service.Name, endpoint.IP, endpoint.Port)) 158 | if err != nil { 159 | r.logger.Errorf(ctx, "UnRegisterService etcd del err, service %v was not registered", service.Name) 160 | } 161 | isRemove = true 162 | break 163 | } 164 | } 165 | 166 | if !isRemove { 167 | endpoints = append(endpoints, endpoint) 168 | } 169 | } 170 | 171 | if len(endpoints) == 0 { 172 | delete(r.registerServices, service.Name) 173 | } else { 174 | r.registerServices[service.Name].service.Endpoints = endpoints 175 | } 176 | } 177 | 178 | func (r *Register) KeepAlive(ctx context.Context, service *registerService) { 179 | for { 180 | select { 181 | case _ = <-service.keepAliveCh: 182 | default: 183 | return 184 | } 185 | } 186 | } 187 | 188 | func (r *Register) Name() string { 189 | return "etcd" 190 | } 191 | 192 | func (r *Register) Register(ctx context.Context, service *discov.Service) { 193 | r.serviceRegisterCh <- service 194 | } 195 | 196 | func (r *Register) UnRegister(ctx context.Context, service *discov.Service) { 197 | r.serviceUnRegisterCh <- service 198 | } 199 | 200 | func (r *Register) GetService(ctx context.Context, name string) (*discov.Service, error) { 201 | allServices := r.getDownServices() 202 | if val, ok := allServices[name]; ok { 203 | return val, nil 204 | } 205 | 206 | // 防止并发获取service导致cache中的数据混乱 207 | r.lock.Lock() 208 | defer r.lock.Unlock() 209 | 210 | // check lock check 防止大量并发进来,unlock后,大量请求etcd 211 | allServices = r.getDownServices() 212 | if val, ok := allServices[name]; ok { 213 | return val, nil 214 | } 215 | 216 | key := r.getEtcdRegisterPrefixKey(name) 217 | getResp, err := r.cli.Get(ctx, key, clientv3.WithPrefix()) 218 | if err != nil { 219 | return nil, err 220 | } 221 | service := &discov.Service{ 222 | Name: name, 223 | Endpoints: make([]*discov.Endpoint, 0), 224 | } 225 | 226 | for _, item := range getResp.Kvs { 227 | var endpoint discov.Endpoint 228 | if err := json.Unmarshal(item.Value, &endpoint); err != nil { 229 | continue 230 | } 231 | 232 | service.Endpoints = append(service.Endpoints, &endpoint) 233 | } 234 | 235 | allServices[name] = service 236 | r.downServices.Store(allServices) 237 | 238 | go r.watch(ctx, key, getResp.Header.Revision+1) 239 | 240 | return service, nil 241 | } 242 | 243 | func (r *Register) watch(ctx context.Context, key string, revision int64) { 244 | rch := r.cli.Watch(ctx, key, clientv3.WithRev(revision), clientv3.WithPrefix()) 245 | for n := range rch { 246 | for _, ev := range n.Events { 247 | switch ev.Type { 248 | case clientv3.EventTypePut: 249 | var endpoint discov.Endpoint 250 | if err := json.Unmarshal(ev.Kv.Value, &endpoint); err != nil { 251 | continue 252 | } 253 | serviceName, _, _ := r.getServiceNameByETCDKey(string(ev.Kv.Key)) 254 | r.updateDownService(&discov.Service{ 255 | Name: serviceName, 256 | Endpoints: []*discov.Endpoint{&endpoint}, 257 | }) 258 | case clientv3.EventTypeDelete: 259 | var endpoint discov.Service 260 | if err := json.Unmarshal(ev.Kv.Value, &endpoint); err != nil { 261 | continue 262 | } 263 | serviceName, ip, Port := r.getServiceNameByETCDKey(string(ev.Kv.Key)) 264 | r.delDownService(&discov.Service{ 265 | Name: serviceName, 266 | Endpoints: []*discov.Endpoint{ 267 | { 268 | IP: ip, 269 | Port: Port, 270 | }, 271 | }, 272 | }) 273 | } 274 | } 275 | } 276 | } 277 | 278 | func (r *Register) updateDownService(service *discov.Service) { 279 | r.lock.Lock() 280 | defer r.lock.Unlock() 281 | 282 | downServices := r.downServices.Load().(map[string]*discov.Service) 283 | if _, ok := downServices[service.Name]; !ok { 284 | downServices[service.Name] = service 285 | r.downServices.Store(downServices) 286 | return 287 | } 288 | 289 | for _, newAddEndpoint := range service.Endpoints { 290 | var isExist bool 291 | for idx, endpoint := range downServices[service.Name].Endpoints { 292 | if newAddEndpoint.IP == endpoint.IP && newAddEndpoint.Port == endpoint.Port { 293 | downServices[service.Name].Endpoints[idx] = newAddEndpoint 294 | isExist = true 295 | break 296 | } 297 | } 298 | 299 | if !isExist { 300 | downServices[service.Name].Endpoints = append(downServices[service.Name].Endpoints, newAddEndpoint) 301 | } 302 | } 303 | 304 | r.downServices.Store(downServices) 305 | } 306 | 307 | func (r *Register) delDownService(service *discov.Service) { 308 | r.lock.Lock() 309 | defer r.lock.Unlock() 310 | 311 | downServices := r.downServices.Load().(map[string]*discov.Service) 312 | if _, ok := downServices[service.Name]; !ok { 313 | return 314 | } 315 | 316 | endpoints := make([]*discov.Endpoint, 0) 317 | for _, endpoint := range downServices[service.Name].Endpoints { 318 | var isRemove bool 319 | for _, delEndpoint := range service.Endpoints { 320 | if delEndpoint.IP == endpoint.IP && delEndpoint.Port == endpoint.Port { 321 | isRemove = true 322 | break 323 | } 324 | } 325 | 326 | if !isRemove { 327 | endpoints = append(endpoints, endpoint) 328 | } 329 | } 330 | 331 | downServices[service.Name].Endpoints = endpoints 332 | r.downServices.Store(downServices) 333 | } 334 | 335 | func (r *Register) getDownServices() map[string]*discov.Service { 336 | allServices := r.downServices.Load() 337 | if allServices == nil { 338 | return make(map[string]*discov.Service, 0) 339 | } 340 | 341 | return allServices.(map[string]*discov.Service) 342 | } 343 | 344 | func (r *Register) getEtcdRegisterKey(name, ip string, port int) string { 345 | return fmt.Sprintf(KeyPrefix+"%v/%v/%v", name, ip, port) 346 | } 347 | 348 | func (r *Register) getEtcdRegisterPrefixKey(name string) string { 349 | return fmt.Sprintf(KeyPrefix+"%v", name) 350 | } 351 | 352 | func (r *Register) getServiceNameByETCDKey(key string) (string, string, int) { 353 | trimStr := strings.TrimLeft(key, KeyPrefix) 354 | strs := strings.Split(trimStr, "/") 355 | 356 | ip, _ := strconv.Atoi(strs[2]) 357 | return strs[0], strs[1], ip 358 | } 359 | -------------------------------------------------------------------------------- /discov/etcd/etcd_register_test.go: -------------------------------------------------------------------------------- 1 | package etcd 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "github.com/wsx864321/srpc/discov" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestNewETCDRegister(t *testing.T) { 14 | NewETCDRegister() 15 | } 16 | 17 | func TestRegister_Register(t *testing.T) { 18 | register := NewETCDRegister(WithEndpoints([]string{"127.0.0.1:2371"})) 19 | 20 | service := &discov.Service{ 21 | Name: "test", 22 | Endpoints: []*discov.Endpoint{ 23 | { 24 | ServiceName: "test", 25 | IP: "127.0.0.1", 26 | Port: 9557, 27 | Enable: true, 28 | }, 29 | }, 30 | } 31 | register.Register(context.TODO(), service) 32 | time.Sleep(2 * time.Second) 33 | registerService, _ := register.GetService(context.TODO(), "test") 34 | 35 | assert.Equal(t, *service.Endpoints[0], *registerService.Endpoints[0]) 36 | } 37 | -------------------------------------------------------------------------------- /discov/etcd/option.go: -------------------------------------------------------------------------------- 1 | package etcd 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/wsx864321/srpc/logger" 7 | ) 8 | 9 | var ( 10 | defaultOption = Options{ 11 | endpoints: []string{"127.0.0.1:2379"}, 12 | dialTimeout: 10 * time.Second, 13 | syncFlushCacheInterval: 10 * time.Second, 14 | keepAliveInterval: 10, 15 | registerServiceOrKeepAliveInterval: 50 * time.Millisecond, 16 | logger: logger.NewSweetLog(), 17 | } 18 | ) 19 | 20 | type Options struct { 21 | syncFlushCacheInterval time.Duration 22 | endpoints []string 23 | dialTimeout time.Duration 24 | keepAliveInterval int64 25 | registerServiceOrKeepAliveInterval time.Duration 26 | logger logger.Log 27 | } 28 | 29 | type Option func(o *Options) 30 | 31 | // WithEndpoints ... 32 | func WithEndpoints(endpoints []string) Option { 33 | return func(o *Options) { 34 | o.endpoints = endpoints 35 | } 36 | } 37 | 38 | // WithDialTimeout ... 39 | func WithDialTimeout(dialTimeout time.Duration) Option { 40 | return func(o *Options) { 41 | o.dialTimeout = dialTimeout 42 | } 43 | } 44 | 45 | // WithSyncFlushCacheInterval ... 46 | func WithSyncFlushCacheInterval(t time.Duration) Option { 47 | return func(o *Options) { 48 | o.syncFlushCacheInterval = t 49 | } 50 | } 51 | 52 | // WithKeepAliveInterval ... 53 | func WithKeepAliveInterval(ttl int64) Option { 54 | return func(o *Options) { 55 | o.keepAliveInterval = ttl 56 | } 57 | } 58 | 59 | // WithRegisterServiceOrKeepAliveInterval ... 60 | func WithRegisterServiceOrKeepAliveInterval(t time.Duration) Option { 61 | return func(o *Options) { 62 | o.registerServiceOrKeepAliveInterval = t 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /discov/service.go: -------------------------------------------------------------------------------- 1 | package discov 2 | 3 | import "fmt" 4 | 5 | type Service struct { 6 | Name string `json:"name"` 7 | Endpoints []*Endpoint `json:"endpoints"` 8 | } 9 | 10 | // Endpoint 增加序列化和传输协议字段(序列化协议交由server端去定义是更加合理的) 11 | type Endpoint struct { 12 | //InstanceID string `json:"instance_id"` 13 | ServiceName string `json:"service_name"` 14 | IP string `json:"ip"` 15 | Port int `json:"port"` 16 | Weight int `json:"weight"` 17 | Network string `json:"network"` 18 | Serialize string `json:"serialize"` 19 | Enable bool `json:"enable"` 20 | } 21 | 22 | func (e *Endpoint) GetAddr() string { 23 | return fmt.Sprintf("%s:%d", e.IP, e.Port) 24 | } 25 | -------------------------------------------------------------------------------- /err/code.go: -------------------------------------------------------------------------------- 1 | package err 2 | 3 | const ( 4 | Ok = 0 5 | 6 | UnKnowErr = 1000 7 | ServerErr = 1001 8 | ServiceNotExistCode = 1002 9 | MethodNotExistCode = 1003 10 | ) 11 | 12 | var ( 13 | OkErr = NewError(Ok, "") 14 | ServiceNotExistErr = NewError(ServiceNotExistCode, "server not exist") 15 | MethodNotExistErr = NewError(MethodNotExistCode, "method not exist") 16 | ) 17 | 18 | func NewError(code int32, msg string) *Error { 19 | return &Error{ 20 | Code: code, 21 | Msg: msg, 22 | } 23 | } 24 | 25 | func (e *Error) Error() string { 26 | return e.Msg 27 | } 28 | 29 | //func (e *Error) Code() string { 30 | // return fmt.Sprintf() 31 | //} 32 | 33 | func (e *Error) WithData(data []byte) *Error { 34 | return &Error{ 35 | Code: e.Code, 36 | Msg: e.Msg, 37 | Data: data, 38 | } 39 | } 40 | 41 | func FromError(err error) (s *Error, ok bool) { 42 | if err == nil { 43 | return nil, true 44 | } 45 | if se, ok := err.(*Error); ok { 46 | return se, ok 47 | } 48 | 49 | return nil, false 50 | } 51 | -------------------------------------------------------------------------------- /err/err.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go v1.28.1 4 | // protoc v3.21.12 5 | // source: err.proto 6 | 7 | package err 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 Error struct { 24 | state protoimpl.MessageState 25 | sizeCache protoimpl.SizeCache 26 | unknownFields protoimpl.UnknownFields 27 | 28 | Code int32 `protobuf:"varint,1,opt,name=Code,proto3" json:"Code,omitempty"` 29 | Msg string `protobuf:"bytes,2,opt,name=Msg,proto3" json:"Msg,omitempty"` 30 | Data []byte `protobuf:"bytes,3,opt,name=Data,proto3" json:"Data,omitempty"` 31 | } 32 | 33 | func (x *Error) Reset() { 34 | *x = Error{} 35 | if protoimpl.UnsafeEnabled { 36 | mi := &file_err_proto_msgTypes[0] 37 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 38 | ms.StoreMessageInfo(mi) 39 | } 40 | } 41 | 42 | func (x *Error) String() string { 43 | return protoimpl.X.MessageStringOf(x) 44 | } 45 | 46 | func (*Error) ProtoMessage() {} 47 | 48 | func (x *Error) ProtoReflect() protoreflect.Message { 49 | mi := &file_err_proto_msgTypes[0] 50 | if protoimpl.UnsafeEnabled && x != nil { 51 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 52 | if ms.LoadMessageInfo() == nil { 53 | ms.StoreMessageInfo(mi) 54 | } 55 | return ms 56 | } 57 | return mi.MessageOf(x) 58 | } 59 | 60 | // Deprecated: Use Error.ProtoReflect.Descriptor instead. 61 | func (*Error) Descriptor() ([]byte, []int) { 62 | return file_err_proto_rawDescGZIP(), []int{0} 63 | } 64 | 65 | func (x *Error) GetCode() int32 { 66 | if x != nil { 67 | return x.Code 68 | } 69 | return 0 70 | } 71 | 72 | func (x *Error) GetMsg() string { 73 | if x != nil { 74 | return x.Msg 75 | } 76 | return "" 77 | } 78 | 79 | func (x *Error) GetData() []byte { 80 | if x != nil { 81 | return x.Data 82 | } 83 | return nil 84 | } 85 | 86 | var File_err_proto protoreflect.FileDescriptor 87 | 88 | var file_err_proto_rawDesc = []byte{ 89 | 0x0a, 0x09, 0x65, 0x72, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x41, 0x0a, 0x05, 0x45, 90 | 0x72, 0x72, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x43, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 91 | 0x28, 0x05, 0x52, 0x04, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x4d, 0x73, 0x67, 0x18, 92 | 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x4d, 0x73, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x44, 0x61, 93 | 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x44, 0x61, 0x74, 0x61, 0x42, 0x08, 94 | 0x5a, 0x06, 0x2e, 0x2f, 0x3b, 0x65, 0x72, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 95 | } 96 | 97 | var ( 98 | file_err_proto_rawDescOnce sync.Once 99 | file_err_proto_rawDescData = file_err_proto_rawDesc 100 | ) 101 | 102 | func file_err_proto_rawDescGZIP() []byte { 103 | file_err_proto_rawDescOnce.Do(func() { 104 | file_err_proto_rawDescData = protoimpl.X.CompressGZIP(file_err_proto_rawDescData) 105 | }) 106 | return file_err_proto_rawDescData 107 | } 108 | 109 | var file_err_proto_msgTypes = make([]protoimpl.MessageInfo, 1) 110 | var file_err_proto_goTypes = []interface{}{ 111 | (*Error)(nil), // 0: Error 112 | } 113 | var file_err_proto_depIdxs = []int32{ 114 | 0, // [0:0] is the sub-list for method output_type 115 | 0, // [0:0] is the sub-list for method input_type 116 | 0, // [0:0] is the sub-list for extension type_name 117 | 0, // [0:0] is the sub-list for extension extendee 118 | 0, // [0:0] is the sub-list for field type_name 119 | } 120 | 121 | func init() { file_err_proto_init() } 122 | func file_err_proto_init() { 123 | if File_err_proto != nil { 124 | return 125 | } 126 | if !protoimpl.UnsafeEnabled { 127 | file_err_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 128 | switch v := v.(*Error); i { 129 | case 0: 130 | return &v.state 131 | case 1: 132 | return &v.sizeCache 133 | case 2: 134 | return &v.unknownFields 135 | default: 136 | return nil 137 | } 138 | } 139 | } 140 | type x struct{} 141 | out := protoimpl.TypeBuilder{ 142 | File: protoimpl.DescBuilder{ 143 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 144 | RawDescriptor: file_err_proto_rawDesc, 145 | NumEnums: 0, 146 | NumMessages: 1, 147 | NumExtensions: 0, 148 | NumServices: 0, 149 | }, 150 | GoTypes: file_err_proto_goTypes, 151 | DependencyIndexes: file_err_proto_depIdxs, 152 | MessageInfos: file_err_proto_msgTypes, 153 | }.Build() 154 | File_err_proto = out.File 155 | file_err_proto_rawDesc = nil 156 | file_err_proto_goTypes = nil 157 | file_err_proto_depIdxs = nil 158 | } 159 | -------------------------------------------------------------------------------- /err/err.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "./;err"; 4 | 5 | message Error { 6 | int32 Code = 1; 7 | string Msg = 2; 8 | bytes Data = 3; 9 | } -------------------------------------------------------------------------------- /examples/helloworld/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/wsx864321/srpc/client" 9 | "github.com/wsx864321/srpc/discov/etcd" 10 | ) 11 | 12 | type HelloWorldReq struct { 13 | Name string `json:"name"` 14 | } 15 | 16 | type HelloWorldResp struct { 17 | Msg string `json:"msg"` 18 | } 19 | 20 | func main() { 21 | req := &HelloWorldReq{ 22 | Name: "wsx", 23 | } 24 | var resp HelloWorldResp 25 | ctx, _ := context.WithTimeout(context.TODO(), 2*time.Second) 26 | cli := client.NewClient(client.WithServiceName("helloworld"), client.WithDiscovery(etcd.NewETCDRegister(etcd.WithEndpoints([]string{"127.0.0.1:2371"})))) 27 | err := cli.Call(ctx, "SayHello", req, &resp) 28 | fmt.Println(resp, err) 29 | } 30 | -------------------------------------------------------------------------------- /examples/helloworld/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/wsx864321/srpc/discov/etcd" 5 | "github.com/wsx864321/srpc/server" 6 | ) 7 | 8 | func main() { 9 | s := server.NewServer(server.WithDiscovery(etcd.NewETCDRegister(etcd.WithEndpoints([]string{"127.0.0.1:2371"})))) 10 | s.RegisterService("helloworld", &HelloWorld{}) 11 | s.Start() 12 | } 13 | -------------------------------------------------------------------------------- /examples/helloworld/server/service.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | ) 7 | 8 | type HelloWorld struct { 9 | } 10 | 11 | type HelloWorldReq struct { 12 | Name string `json:"name"` 13 | } 14 | 15 | type HelloWorldResp struct { 16 | Msg string `json:"msg"` 17 | } 18 | 19 | func (h *HelloWorld) SayHello(ctx context.Context, req *HelloWorldReq) (*HelloWorldResp, error) { 20 | return &HelloWorldResp{ 21 | Msg: fmt.Sprintf("%s say hello", req.Name), 22 | }, nil 23 | } 24 | -------------------------------------------------------------------------------- /examples/interceptor/timeout/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/wsx864321/srpc/interceptor/clientinterceptor" 9 | 10 | "github.com/wsx864321/srpc/client" 11 | "github.com/wsx864321/srpc/discov/etcd" 12 | "github.com/wsx864321/srpc/interceptor" 13 | ) 14 | 15 | type HelloWorldReq struct { 16 | Name string `json:"name"` 17 | } 18 | 19 | type HelloWorldResp struct { 20 | Msg string `json:"msg"` 21 | } 22 | 23 | func main() { 24 | req := &HelloWorldReq{ 25 | Name: "wsx", 26 | } 27 | var resp HelloWorldResp 28 | ctx, _ := context.WithTimeout(context.TODO(), 2*time.Second) 29 | cli := client.NewClient( 30 | client.WithServiceName("helloworld"), 31 | client.WithDiscovery( 32 | etcd.NewETCDRegister( 33 | etcd.WithEndpoints([]string{"127.0.0.1:2371"}), 34 | ), 35 | ), 36 | client.WithInterceptors([]interceptor.ClientInterceptor{clientinterceptor.ClientTimeoutInterceptor()}...), 37 | client.WithReadTimeout(10*time.Second), 38 | client.WithWriteTimeout(10*time.Second), 39 | ) 40 | err := cli.Call(ctx, "SayHello", req, &resp) 41 | fmt.Println(resp, err) 42 | } 43 | -------------------------------------------------------------------------------- /examples/interceptor/timeout/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/wsx864321/srpc/codec/serialize" 5 | "github.com/wsx864321/srpc/discov/etcd" 6 | "github.com/wsx864321/srpc/interceptor" 7 | "github.com/wsx864321/srpc/interceptor/serverinterceptor" 8 | "github.com/wsx864321/srpc/server" 9 | ) 10 | 11 | func main() { 12 | s := server.NewServer( 13 | server.WithSerialize(serialize.SerializeTypeMsgpack), 14 | server.WithDiscovery( 15 | etcd.NewETCDRegister( 16 | etcd.WithEndpoints([]string{"127.0.0.1:2371"}), 17 | ), 18 | ), 19 | ) 20 | s.RegisterService("helloworld", &HelloWorld{}, []interceptor.ServerInterceptor{serverinterceptor.ServerTimeoutInterceptor()}...) 21 | s.Start() 22 | } 23 | -------------------------------------------------------------------------------- /examples/interceptor/timeout/server/service.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | type HelloWorld struct { 10 | } 11 | 12 | type HelloWorldReq struct { 13 | Name string `json:"name"` 14 | } 15 | 16 | type HelloWorldResp struct { 17 | Msg string `json:"msg"` 18 | } 19 | 20 | func (h *HelloWorld) SayHello(ctx context.Context, req *HelloWorldReq) (*HelloWorldResp, error) { 21 | time.Sleep(3 * time.Second) 22 | return &HelloWorldResp{ 23 | Msg: fmt.Sprintf("%s say hello", req.Name), 24 | }, nil 25 | } 26 | -------------------------------------------------------------------------------- /examples/interceptor/trace/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | strace "github.com/wsx864321/srpc/trace" 9 | 10 | "github.com/wsx864321/srpc/interceptor/clientinterceptor" 11 | 12 | "github.com/wsx864321/srpc/client" 13 | "github.com/wsx864321/srpc/discov/etcd" 14 | "github.com/wsx864321/srpc/interceptor" 15 | ) 16 | 17 | type HelloWorldReq struct { 18 | Name string `json:"name"` 19 | } 20 | 21 | type HelloWorldResp struct { 22 | Msg string `json:"msg"` 23 | } 24 | 25 | func main() { 26 | strace.StartAgent(strace.WithServiceName("helloworld-client")) 27 | defer strace.StopAgent() 28 | 29 | req := &HelloWorldReq{ 30 | Name: "wsx", 31 | } 32 | var resp HelloWorldResp 33 | ctx, _ := context.WithTimeout(context.TODO(), 2*time.Second) 34 | cli := client.NewClient( 35 | client.WithServiceName("helloworld"), 36 | client.WithDiscovery( 37 | etcd.NewETCDRegister( 38 | etcd.WithEndpoints([]string{"127.0.0.1:2371"}), 39 | ), 40 | ), 41 | client.WithInterceptors([]interceptor.ClientInterceptor{clientinterceptor.ClientTraceInterceptor(), clientinterceptor.ClientTimeoutInterceptor()}...), 42 | client.WithReadTimeout(10*time.Second), 43 | client.WithWriteTimeout(10*time.Second), 44 | ) 45 | err := cli.Call(ctx, "SayHello", req, &resp) 46 | fmt.Println(resp, err) 47 | } 48 | -------------------------------------------------------------------------------- /examples/interceptor/trace/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/wsx864321/srpc/codec/serialize" 5 | "github.com/wsx864321/srpc/discov/etcd" 6 | "github.com/wsx864321/srpc/interceptor" 7 | "github.com/wsx864321/srpc/interceptor/serverinterceptor" 8 | "github.com/wsx864321/srpc/server" 9 | strace "github.com/wsx864321/srpc/trace" 10 | ) 11 | 12 | func main() { 13 | strace.StartAgent(strace.WithServiceName("helloworld-server")) 14 | defer strace.StopAgent() 15 | 16 | s := server.NewServer( 17 | server.WithSerialize(serialize.SerializeTypeMsgpack), 18 | server.WithDiscovery( 19 | etcd.NewETCDRegister( 20 | etcd.WithEndpoints([]string{"127.0.0.1:2371"}), 21 | ), 22 | ), 23 | ) 24 | s.RegisterService("helloworld", &HelloWorld{}, []interceptor.ServerInterceptor{serverinterceptor.ServerTraceInterceptor(), serverinterceptor.ServerTimeoutInterceptor()}...) 25 | s.Start() 26 | } 27 | -------------------------------------------------------------------------------- /examples/interceptor/trace/server/service.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | type HelloWorld struct { 10 | } 11 | 12 | type HelloWorldReq struct { 13 | Name string `json:"name"` 14 | } 15 | 16 | type HelloWorldResp struct { 17 | Msg string `json:"msg"` 18 | } 19 | 20 | func (h *HelloWorld) SayHello(ctx context.Context, req *HelloWorldReq) (*HelloWorldResp, error) { 21 | time.Sleep(500 * time.Millisecond) 22 | return &HelloWorldResp{ 23 | Msg: fmt.Sprintf("%s say hello", req.Name), 24 | }, nil 25 | } 26 | -------------------------------------------------------------------------------- /examples/metadata/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/wsx864321/srpc/metadata" 9 | 10 | "github.com/wsx864321/srpc/client" 11 | "github.com/wsx864321/srpc/discov/etcd" 12 | ) 13 | 14 | type HelloWorldReq struct { 15 | Name string `json:"name"` 16 | } 17 | 18 | type HelloWorldResp struct { 19 | Msg string `json:"msg"` 20 | } 21 | 22 | func main() { 23 | req := &HelloWorldReq{ 24 | Name: "wsx", 25 | } 26 | var resp HelloWorldResp 27 | ctx, _ := context.WithTimeout(context.TODO(), 2*time.Second) 28 | ctx = metadata.WithClientMetadata(ctx, map[string]string{ 29 | "token": "test", 30 | }) 31 | cli := client.NewClient(client.WithServiceName("helloworld"), client.WithDiscovery(etcd.NewETCDRegister(etcd.WithEndpoints([]string{"127.0.0.1:2371"})))) 32 | err := cli.Call(ctx, "SayHello", req, &resp) 33 | fmt.Println(resp, err) 34 | } 35 | -------------------------------------------------------------------------------- /examples/metadata/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/wsx864321/srpc/discov/etcd" 5 | "github.com/wsx864321/srpc/server" 6 | ) 7 | 8 | func main() { 9 | s := server.NewServer(server.WithDiscovery(etcd.NewETCDRegister(etcd.WithEndpoints([]string{"127.0.0.1:2371"})))) 10 | s.RegisterService("helloworld", &HelloWorld{}) 11 | s.Start() 12 | } 13 | -------------------------------------------------------------------------------- /examples/metadata/server/service.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/wsx864321/srpc/metadata" 7 | ) 8 | 9 | type HelloWorld struct { 10 | } 11 | 12 | type HelloWorldReq struct { 13 | Name string `json:"name"` 14 | } 15 | 16 | type HelloWorldResp struct { 17 | Msg string `json:"msg"` 18 | } 19 | 20 | func (h *HelloWorld) SayHello(ctx context.Context, req *HelloWorldReq) (*HelloWorldResp, error) { 21 | metaData := metadata.ExtractServerMetadata(ctx) 22 | return &HelloWorldResp{ 23 | Msg: metaData["token"], 24 | }, nil 25 | } 26 | -------------------------------------------------------------------------------- /examples/msgpack/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/wsx864321/srpc/client" 9 | "github.com/wsx864321/srpc/discov/etcd" 10 | ) 11 | 12 | type HelloWorldReq struct { 13 | Name string `json:"name"` 14 | } 15 | 16 | type HelloWorldResp struct { 17 | Msg string `json:"msg"` 18 | } 19 | 20 | func main() { 21 | req := &HelloWorldReq{ 22 | Name: "wsx", 23 | } 24 | var resp HelloWorldResp 25 | ctx, _ := context.WithTimeout(context.TODO(), 2*time.Second) 26 | cli := client.NewClient(client.WithServiceName("helloworld"), client.WithDiscovery(etcd.NewETCDRegister(etcd.WithEndpoints([]string{"127.0.0.1:2371"})))) 27 | err := cli.Call(ctx, "SayHello", req, &resp) 28 | fmt.Println(resp, err) 29 | } 30 | -------------------------------------------------------------------------------- /examples/msgpack/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/wsx864321/srpc/codec/serialize" 5 | "github.com/wsx864321/srpc/discov/etcd" 6 | "github.com/wsx864321/srpc/server" 7 | ) 8 | 9 | func main() { 10 | s := server.NewServer(server.WithSerialize(serialize.SerializeTypeMsgpack), server.WithDiscovery(etcd.NewETCDRegister(etcd.WithEndpoints([]string{"127.0.0.1:2371"})))) 11 | s.RegisterService("helloworld", &HelloWorld{}) 12 | s.Start() 13 | } 14 | -------------------------------------------------------------------------------- /examples/msgpack/server/service.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | ) 7 | 8 | type HelloWorld struct { 9 | } 10 | 11 | type HelloWorldReq struct { 12 | Name string `json:"name"` 13 | } 14 | 15 | type HelloWorldResp struct { 16 | Msg string `json:"msg"` 17 | } 18 | 19 | func (h *HelloWorld) SayHello(ctx context.Context, req *HelloWorldReq) (*HelloWorldResp, error) { 20 | return &HelloWorldResp{ 21 | Msg: fmt.Sprintf("%s say hello", req.Name), 22 | }, nil 23 | } 24 | -------------------------------------------------------------------------------- /examples/proto/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/wsx864321/srpc/client" 9 | "github.com/wsx864321/srpc/discov/etcd" 10 | ) 11 | 12 | func main() { 13 | req := &HelloWorldReq{ 14 | Name: "wsx", 15 | } 16 | var resp HelloWorldResp 17 | ctx, _ := context.WithTimeout(context.TODO(), 2*time.Second) 18 | cli := client.NewClient(client.WithServiceName("helloworld"), client.WithDiscovery(etcd.NewETCDRegister(etcd.WithEndpoints([]string{"127.0.0.1:2371"})))) 19 | err := cli.Call(ctx, "SayHello", req, &resp) 20 | fmt.Println(resp, err) 21 | } 22 | -------------------------------------------------------------------------------- /examples/proto/client/hellowordl.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go v1.28.1 4 | // protoc v3.21.12 5 | // source: hellowordl.proto 6 | 7 | package main 8 | 9 | import ( 10 | reflect "reflect" 11 | sync "sync" 12 | 13 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 14 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 15 | ) 16 | 17 | const ( 18 | // Verify that this generated code is sufficiently up-to-date. 19 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 20 | // Verify that runtime/protoimpl is sufficiently up-to-date. 21 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 22 | ) 23 | 24 | type HelloWorldReq struct { 25 | state protoimpl.MessageState 26 | sizeCache protoimpl.SizeCache 27 | unknownFields protoimpl.UnknownFields 28 | 29 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 30 | } 31 | 32 | func (x *HelloWorldReq) Reset() { 33 | *x = HelloWorldReq{} 34 | if protoimpl.UnsafeEnabled { 35 | mi := &file_hellowordl_proto_msgTypes[0] 36 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 37 | ms.StoreMessageInfo(mi) 38 | } 39 | } 40 | 41 | func (x *HelloWorldReq) String() string { 42 | return protoimpl.X.MessageStringOf(x) 43 | } 44 | 45 | func (*HelloWorldReq) ProtoMessage() {} 46 | 47 | func (x *HelloWorldReq) ProtoReflect() protoreflect.Message { 48 | mi := &file_hellowordl_proto_msgTypes[0] 49 | if protoimpl.UnsafeEnabled && x != nil { 50 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 51 | if ms.LoadMessageInfo() == nil { 52 | ms.StoreMessageInfo(mi) 53 | } 54 | return ms 55 | } 56 | return mi.MessageOf(x) 57 | } 58 | 59 | // Deprecated: Use HelloWorldReq.ProtoReflect.Descriptor instead. 60 | func (*HelloWorldReq) Descriptor() ([]byte, []int) { 61 | return file_hellowordl_proto_rawDescGZIP(), []int{0} 62 | } 63 | 64 | func (x *HelloWorldReq) GetName() string { 65 | if x != nil { 66 | return x.Name 67 | } 68 | return "" 69 | } 70 | 71 | type HelloWorldResp struct { 72 | state protoimpl.MessageState 73 | sizeCache protoimpl.SizeCache 74 | unknownFields protoimpl.UnknownFields 75 | 76 | Msg string `protobuf:"bytes,1,opt,name=Msg,proto3" json:"Msg,omitempty"` 77 | } 78 | 79 | func (x *HelloWorldResp) Reset() { 80 | *x = HelloWorldResp{} 81 | if protoimpl.UnsafeEnabled { 82 | mi := &file_hellowordl_proto_msgTypes[1] 83 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 84 | ms.StoreMessageInfo(mi) 85 | } 86 | } 87 | 88 | func (x *HelloWorldResp) String() string { 89 | return protoimpl.X.MessageStringOf(x) 90 | } 91 | 92 | func (*HelloWorldResp) ProtoMessage() {} 93 | 94 | func (x *HelloWorldResp) ProtoReflect() protoreflect.Message { 95 | mi := &file_hellowordl_proto_msgTypes[1] 96 | if protoimpl.UnsafeEnabled && x != nil { 97 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 98 | if ms.LoadMessageInfo() == nil { 99 | ms.StoreMessageInfo(mi) 100 | } 101 | return ms 102 | } 103 | return mi.MessageOf(x) 104 | } 105 | 106 | // Deprecated: Use HelloWorldResp.ProtoReflect.Descriptor instead. 107 | func (*HelloWorldResp) Descriptor() ([]byte, []int) { 108 | return file_hellowordl_proto_rawDescGZIP(), []int{1} 109 | } 110 | 111 | func (x *HelloWorldResp) GetMsg() string { 112 | if x != nil { 113 | return x.Msg 114 | } 115 | return "" 116 | } 117 | 118 | var File_hellowordl_proto protoreflect.FileDescriptor 119 | 120 | var file_hellowordl_proto_rawDesc = []byte{ 121 | 0x0a, 0x10, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x64, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 122 | 0x74, 0x6f, 0x22, 0x23, 0x0a, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x57, 0x6f, 0x72, 0x6c, 0x64, 123 | 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 124 | 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x22, 0x0a, 0x0e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 125 | 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x4d, 0x73, 0x67, 126 | 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x4d, 0x73, 0x67, 0x42, 0x09, 0x5a, 0x07, 0x2e, 127 | 0x2f, 0x3b, 0x6d, 0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 128 | } 129 | 130 | var ( 131 | file_hellowordl_proto_rawDescOnce sync.Once 132 | file_hellowordl_proto_rawDescData = file_hellowordl_proto_rawDesc 133 | ) 134 | 135 | func file_hellowordl_proto_rawDescGZIP() []byte { 136 | file_hellowordl_proto_rawDescOnce.Do(func() { 137 | file_hellowordl_proto_rawDescData = protoimpl.X.CompressGZIP(file_hellowordl_proto_rawDescData) 138 | }) 139 | return file_hellowordl_proto_rawDescData 140 | } 141 | 142 | var file_hellowordl_proto_msgTypes = make([]protoimpl.MessageInfo, 2) 143 | var file_hellowordl_proto_goTypes = []interface{}{ 144 | (*HelloWorldReq)(nil), // 0: HelloWorldReq 145 | (*HelloWorldResp)(nil), // 1: HelloWorldResp 146 | } 147 | var file_hellowordl_proto_depIdxs = []int32{ 148 | 0, // [0:0] is the sub-list for method output_type 149 | 0, // [0:0] is the sub-list for method input_type 150 | 0, // [0:0] is the sub-list for extension type_name 151 | 0, // [0:0] is the sub-list for extension extendee 152 | 0, // [0:0] is the sub-list for field type_name 153 | } 154 | 155 | func init() { file_hellowordl_proto_init() } 156 | func file_hellowordl_proto_init() { 157 | if File_hellowordl_proto != nil { 158 | return 159 | } 160 | if !protoimpl.UnsafeEnabled { 161 | file_hellowordl_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 162 | switch v := v.(*HelloWorldReq); i { 163 | case 0: 164 | return &v.state 165 | case 1: 166 | return &v.sizeCache 167 | case 2: 168 | return &v.unknownFields 169 | default: 170 | return nil 171 | } 172 | } 173 | file_hellowordl_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { 174 | switch v := v.(*HelloWorldResp); i { 175 | case 0: 176 | return &v.state 177 | case 1: 178 | return &v.sizeCache 179 | case 2: 180 | return &v.unknownFields 181 | default: 182 | return nil 183 | } 184 | } 185 | } 186 | type x struct{} 187 | out := protoimpl.TypeBuilder{ 188 | File: protoimpl.DescBuilder{ 189 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 190 | RawDescriptor: file_hellowordl_proto_rawDesc, 191 | NumEnums: 0, 192 | NumMessages: 2, 193 | NumExtensions: 0, 194 | NumServices: 0, 195 | }, 196 | GoTypes: file_hellowordl_proto_goTypes, 197 | DependencyIndexes: file_hellowordl_proto_depIdxs, 198 | MessageInfos: file_hellowordl_proto_msgTypes, 199 | }.Build() 200 | File_hellowordl_proto = out.File 201 | file_hellowordl_proto_rawDesc = nil 202 | file_hellowordl_proto_goTypes = nil 203 | file_hellowordl_proto_depIdxs = nil 204 | } 205 | -------------------------------------------------------------------------------- /examples/proto/server/hellowordl.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go v1.28.1 4 | // protoc v3.21.12 5 | // source: hellowordl.proto 6 | 7 | package main 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 HelloWorldReq 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 *HelloWorldReq) Reset() { 32 | *x = HelloWorldReq{} 33 | if protoimpl.UnsafeEnabled { 34 | mi := &file_hellowordl_proto_msgTypes[0] 35 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 36 | ms.StoreMessageInfo(mi) 37 | } 38 | } 39 | 40 | func (x *HelloWorldReq) String() string { 41 | return protoimpl.X.MessageStringOf(x) 42 | } 43 | 44 | func (*HelloWorldReq) ProtoMessage() {} 45 | 46 | func (x *HelloWorldReq) ProtoReflect() protoreflect.Message { 47 | mi := &file_hellowordl_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 HelloWorldReq.ProtoReflect.Descriptor instead. 59 | func (*HelloWorldReq) Descriptor() ([]byte, []int) { 60 | return file_hellowordl_proto_rawDescGZIP(), []int{0} 61 | } 62 | 63 | func (x *HelloWorldReq) GetName() string { 64 | if x != nil { 65 | return x.Name 66 | } 67 | return "" 68 | } 69 | 70 | type HelloWorldResp struct { 71 | state protoimpl.MessageState 72 | sizeCache protoimpl.SizeCache 73 | unknownFields protoimpl.UnknownFields 74 | 75 | Msg string `protobuf:"bytes,1,opt,name=Msg,proto3" json:"Msg,omitempty"` 76 | } 77 | 78 | func (x *HelloWorldResp) Reset() { 79 | *x = HelloWorldResp{} 80 | if protoimpl.UnsafeEnabled { 81 | mi := &file_hellowordl_proto_msgTypes[1] 82 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 83 | ms.StoreMessageInfo(mi) 84 | } 85 | } 86 | 87 | func (x *HelloWorldResp) String() string { 88 | return protoimpl.X.MessageStringOf(x) 89 | } 90 | 91 | func (*HelloWorldResp) ProtoMessage() {} 92 | 93 | func (x *HelloWorldResp) ProtoReflect() protoreflect.Message { 94 | mi := &file_hellowordl_proto_msgTypes[1] 95 | if protoimpl.UnsafeEnabled && x != nil { 96 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 97 | if ms.LoadMessageInfo() == nil { 98 | ms.StoreMessageInfo(mi) 99 | } 100 | return ms 101 | } 102 | return mi.MessageOf(x) 103 | } 104 | 105 | // Deprecated: Use HelloWorldResp.ProtoReflect.Descriptor instead. 106 | func (*HelloWorldResp) Descriptor() ([]byte, []int) { 107 | return file_hellowordl_proto_rawDescGZIP(), []int{1} 108 | } 109 | 110 | func (x *HelloWorldResp) GetMsg() string { 111 | if x != nil { 112 | return x.Msg 113 | } 114 | return "" 115 | } 116 | 117 | var File_hellowordl_proto protoreflect.FileDescriptor 118 | 119 | var file_hellowordl_proto_rawDesc = []byte{ 120 | 0x0a, 0x10, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x64, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 121 | 0x74, 0x6f, 0x22, 0x23, 0x0a, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x57, 0x6f, 0x72, 0x6c, 0x64, 122 | 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 123 | 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x22, 0x0a, 0x0e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 124 | 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x4d, 0x73, 0x67, 125 | 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x4d, 0x73, 0x67, 0x42, 0x09, 0x5a, 0x07, 0x2e, 126 | 0x2f, 0x3b, 0x6d, 0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 127 | } 128 | 129 | var ( 130 | file_hellowordl_proto_rawDescOnce sync.Once 131 | file_hellowordl_proto_rawDescData = file_hellowordl_proto_rawDesc 132 | ) 133 | 134 | func file_hellowordl_proto_rawDescGZIP() []byte { 135 | file_hellowordl_proto_rawDescOnce.Do(func() { 136 | file_hellowordl_proto_rawDescData = protoimpl.X.CompressGZIP(file_hellowordl_proto_rawDescData) 137 | }) 138 | return file_hellowordl_proto_rawDescData 139 | } 140 | 141 | var file_hellowordl_proto_msgTypes = make([]protoimpl.MessageInfo, 2) 142 | var file_hellowordl_proto_goTypes = []interface{}{ 143 | (*HelloWorldReq)(nil), // 0: HelloWorldReq 144 | (*HelloWorldResp)(nil), // 1: HelloWorldResp 145 | } 146 | var file_hellowordl_proto_depIdxs = []int32{ 147 | 0, // [0:0] is the sub-list for method output_type 148 | 0, // [0:0] is the sub-list for method input_type 149 | 0, // [0:0] is the sub-list for extension type_name 150 | 0, // [0:0] is the sub-list for extension extendee 151 | 0, // [0:0] is the sub-list for field type_name 152 | } 153 | 154 | func init() { file_hellowordl_proto_init() } 155 | func file_hellowordl_proto_init() { 156 | if File_hellowordl_proto != nil { 157 | return 158 | } 159 | if !protoimpl.UnsafeEnabled { 160 | file_hellowordl_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 161 | switch v := v.(*HelloWorldReq); i { 162 | case 0: 163 | return &v.state 164 | case 1: 165 | return &v.sizeCache 166 | case 2: 167 | return &v.unknownFields 168 | default: 169 | return nil 170 | } 171 | } 172 | file_hellowordl_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { 173 | switch v := v.(*HelloWorldResp); i { 174 | case 0: 175 | return &v.state 176 | case 1: 177 | return &v.sizeCache 178 | case 2: 179 | return &v.unknownFields 180 | default: 181 | return nil 182 | } 183 | } 184 | } 185 | type x struct{} 186 | out := protoimpl.TypeBuilder{ 187 | File: protoimpl.DescBuilder{ 188 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 189 | RawDescriptor: file_hellowordl_proto_rawDesc, 190 | NumEnums: 0, 191 | NumMessages: 2, 192 | NumExtensions: 0, 193 | NumServices: 0, 194 | }, 195 | GoTypes: file_hellowordl_proto_goTypes, 196 | DependencyIndexes: file_hellowordl_proto_depIdxs, 197 | MessageInfos: file_hellowordl_proto_msgTypes, 198 | }.Build() 199 | File_hellowordl_proto = out.File 200 | file_hellowordl_proto_rawDesc = nil 201 | file_hellowordl_proto_goTypes = nil 202 | file_hellowordl_proto_depIdxs = nil 203 | } 204 | -------------------------------------------------------------------------------- /examples/proto/server/hellowordl.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "./;main"; 4 | 5 | message HelloWorldReq { 6 | string name = 1; 7 | } 8 | 9 | message HelloWorldResp { 10 | string Msg = 1; 11 | } -------------------------------------------------------------------------------- /examples/proto/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/wsx864321/srpc/codec/serialize" 5 | "github.com/wsx864321/srpc/discov/etcd" 6 | "github.com/wsx864321/srpc/server" 7 | ) 8 | 9 | func main() { 10 | s := server.NewServer(server.WithSerialize(serialize.SerializeTypeProto), server.WithDiscovery(etcd.NewETCDRegister(etcd.WithEndpoints([]string{"127.0.0.1:2371"})))) 11 | s.RegisterService("helloworld", &HelloWorld{}) 12 | s.Start() 13 | } 14 | -------------------------------------------------------------------------------- /examples/proto/server/service.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | ) 7 | 8 | type HelloWorld struct { 9 | } 10 | 11 | func (h *HelloWorld) SayHello(ctx context.Context, req *HelloWorldReq) (*HelloWorldResp, error) { 12 | fmt.Println(req.Name) 13 | return &HelloWorldResp{ 14 | Msg: fmt.Sprintf("%s say hello", req.Name), 15 | }, nil 16 | } 17 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/wsx864321/srpc 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/gogo/protobuf v1.3.2 7 | github.com/golang/protobuf v1.5.2 8 | github.com/stretchr/testify v1.8.1 9 | github.com/vmihailenco/msgpack v4.0.4+incompatible 10 | go.etcd.io/etcd/client/v3 v3.5.4 11 | go.opentelemetry.io/otel v1.11.2 12 | go.opentelemetry.io/otel/exporters/jaeger v1.11.2 13 | go.opentelemetry.io/otel/sdk v1.11.2 14 | go.opentelemetry.io/otel/trace v1.11.2 15 | google.golang.org/protobuf v1.28.1 16 | ) 17 | 18 | require ( 19 | github.com/coreos/go-semver v0.3.0 // indirect 20 | github.com/coreos/go-systemd/v22 v22.3.2 // indirect 21 | github.com/davecgh/go-spew v1.1.1 // indirect 22 | github.com/go-logr/logr v1.2.3 // indirect 23 | github.com/go-logr/stdr v1.2.2 // indirect 24 | github.com/kr/pretty v0.3.0 // indirect 25 | github.com/pmezard/go-difflib v1.0.0 // indirect 26 | go.etcd.io/etcd/api/v3 v3.5.4 // indirect 27 | go.etcd.io/etcd/client/pkg/v3 v3.5.4 // indirect 28 | go.uber.org/atomic v1.7.0 // indirect 29 | go.uber.org/multierr v1.6.0 // indirect 30 | go.uber.org/zap v1.22.0 // indirect 31 | golang.org/x/net v0.0.0-20220909164309-bea034e7d591 // indirect 32 | golang.org/x/sys v0.0.0-20221010170243-090e33056c14 // indirect 33 | golang.org/x/text v0.3.7 // indirect 34 | google.golang.org/appengine v1.6.7 // indirect 35 | google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de // indirect 36 | google.golang.org/grpc v1.48.0 // indirect 37 | gopkg.in/yaml.v3 v3.0.1 // indirect 38 | ) 39 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 4 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 5 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 6 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 7 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 8 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= 9 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 10 | github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= 11 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 12 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 13 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 14 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 15 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 16 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 17 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 18 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 19 | github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= 20 | github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 21 | github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 22 | github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 23 | github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= 24 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 25 | github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= 26 | github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 27 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 28 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 29 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 30 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 31 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 32 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 33 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 34 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 35 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 36 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 37 | github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= 38 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 39 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 40 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 41 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 42 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 43 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 44 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 45 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 46 | github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 47 | github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= 48 | github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 49 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 50 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 51 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 52 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 53 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 54 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 55 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 56 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 57 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 58 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 59 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 60 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 61 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 62 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 63 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 64 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 65 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 66 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 67 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 68 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 69 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 70 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 71 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 72 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 73 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 74 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 75 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 76 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 77 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 78 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 79 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 80 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 81 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 82 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 83 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 84 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 85 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 86 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 87 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 88 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 89 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 90 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 91 | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 92 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 93 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 94 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 95 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 96 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 97 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 98 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 99 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 100 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 101 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 102 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 103 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 104 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 105 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 106 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 107 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 108 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 109 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 110 | github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 111 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 112 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 113 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 114 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 115 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 116 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 117 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 118 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 119 | github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= 120 | github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= 121 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 122 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 123 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 124 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 125 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 126 | github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= 127 | github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= 128 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 129 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 130 | github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 131 | github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 132 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 133 | github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= 134 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 135 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 136 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 137 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 138 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 139 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 140 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 141 | github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= 142 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 143 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 144 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 145 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 146 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 147 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 148 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 149 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 150 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 151 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 152 | github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= 153 | github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= 154 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 155 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 156 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 157 | go.etcd.io/etcd/api/v3 v3.5.4 h1:OHVyt3TopwtUQ2GKdd5wu3PmmipR4FTwCqoEjSyRdIc= 158 | go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= 159 | go.etcd.io/etcd/client/pkg/v3 v3.5.4 h1:lrneYvz923dvC14R54XcA7FXoZ3mlGZAgmwhfm7HqOg= 160 | go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= 161 | go.etcd.io/etcd/client/v3 v3.5.4 h1:p83BUL3tAYS0OT/r0qglgc3M1JjhM0diV8DSWAhVXv4= 162 | go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= 163 | go.opentelemetry.io/otel v1.11.2 h1:YBZcQlsVekzFsFbjygXMOXSs6pialIZxcjfO/mBDmR0= 164 | go.opentelemetry.io/otel v1.11.2/go.mod h1:7p4EUV+AqgdlNV9gL97IgUZiVR3yrFXYo53f9BM3tRI= 165 | go.opentelemetry.io/otel/exporters/jaeger v1.11.2 h1:ES8/j2+aB+3/BUw51ioxa50V9btN1eew/2J7N7n1tsE= 166 | go.opentelemetry.io/otel/exporters/jaeger v1.11.2/go.mod h1:nwcF/DK4Hk0auZ/a5vw20uMsaJSXbzeeimhN5f9d0Lc= 167 | go.opentelemetry.io/otel/sdk v1.11.2 h1:GF4JoaEx7iihdMFu30sOyRx52HDHOkl9xQ8SMqNXUiU= 168 | go.opentelemetry.io/otel/sdk v1.11.2/go.mod h1:wZ1WxImwpq+lVRo4vsmSOxdd+xwoUJ6rqyLc3SyX9aU= 169 | go.opentelemetry.io/otel/trace v1.11.2 h1:Xf7hWSF2Glv0DE3MH7fBHvtpSBsjcBUe5MYAmZM/+y0= 170 | go.opentelemetry.io/otel/trace v1.11.2/go.mod h1:4N+yC7QEz7TTsG9BSRLNAa63eg5E06ObSbKPmxQ/pKA= 171 | go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= 172 | go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= 173 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 174 | go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= 175 | go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= 176 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 177 | go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= 178 | go.uber.org/zap v1.22.0 h1:Zcye5DUgBloQ9BaT4qc9BnjOFog5TvBSAGkJ3Nf70c0= 179 | go.uber.org/zap v1.22.0/go.mod h1:H4siCOZOrAolnUPJEkfaSjDqyP+BDS0DdDWzwcgt3+U= 180 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 181 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 182 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 183 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 184 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 185 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 186 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 187 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 188 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 189 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 190 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 191 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 192 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 193 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 194 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 195 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 196 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 197 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 198 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 199 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 200 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 201 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 202 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 203 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 204 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 205 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 206 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 207 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 208 | golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI= 209 | golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 210 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 211 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 212 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 213 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 214 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 215 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 216 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 217 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 218 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 219 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 220 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 221 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 222 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 223 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 224 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 225 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 226 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 227 | golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 228 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 229 | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 230 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 231 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 232 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 233 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 234 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 235 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 236 | golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 237 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 238 | golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 239 | golang.org/x/sys v0.0.0-20221010170243-090e33056c14 h1:k5II8e6QD8mITdi+okbbmR/cIyEbeXLBhy5Ha4nevyc= 240 | golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 241 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 242 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 243 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 244 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 245 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 246 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 247 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 248 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 249 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 250 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 251 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 252 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 253 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 254 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 255 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 256 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 257 | golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 258 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 259 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 260 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 261 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 262 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 263 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 264 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 265 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 266 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 267 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 268 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 269 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 270 | google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= 271 | google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de h1:5ANeKFmGdtiputJJYeUVg8nTGA/1bEirx4CgzcnPSx8= 272 | google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= 273 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 274 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 275 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 276 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 277 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 278 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 279 | google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 280 | google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w= 281 | google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= 282 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 283 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 284 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 285 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 286 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 287 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 288 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 289 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 290 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 291 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 292 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 293 | google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 294 | google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= 295 | google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 296 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 297 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 298 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 299 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 300 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 301 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 302 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 303 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 304 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 305 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 306 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 307 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 308 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 309 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 310 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 311 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 312 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 313 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 314 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 315 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 316 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 317 | sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= 318 | -------------------------------------------------------------------------------- /imgs/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wsx864321/srpc/7b6cee394b722924fb0e9401ece694d94d0a37b3/imgs/img.png -------------------------------------------------------------------------------- /imgs/img_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wsx864321/srpc/7b6cee394b722924fb0e9401ece694d94d0a37b3/imgs/img_1.png -------------------------------------------------------------------------------- /interceptor/clientinterceptor/timeoutinterceptor.go: -------------------------------------------------------------------------------- 1 | package clientinterceptor 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/wsx864321/srpc/interceptor" 9 | "github.com/wsx864321/srpc/metadata" 10 | ) 11 | 12 | // ClientTimeoutInterceptor 客户端级联超时控制 13 | func ClientTimeoutInterceptor() interceptor.ClientInterceptor { 14 | return func(ctx context.Context, method, target string, req, resp interface{}, h interceptor.Invoker) error { 15 | if deadline, ok := ctx.Deadline(); ok { 16 | md := metadata.ExtractClientMetadata(ctx) 17 | md[metadata.SRPCTimeout] = fmt.Sprintf("%d", time.Until(deadline).Nanoseconds()) 18 | ctx = metadata.WithClientMetadata(ctx, md) 19 | } 20 | 21 | return h(ctx, req, resp) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /interceptor/clientinterceptor/traceinterceptor.go: -------------------------------------------------------------------------------- 1 | package clientinterceptor 2 | 3 | import ( 4 | "context" 5 | 6 | srpcerr "github.com/wsx864321/srpc/err" 7 | 8 | "github.com/wsx864321/srpc/interceptor" 9 | "github.com/wsx864321/srpc/metadata" 10 | strace "github.com/wsx864321/srpc/trace" 11 | "go.opentelemetry.io/otel" 12 | "go.opentelemetry.io/otel/codes" 13 | "go.opentelemetry.io/otel/trace" 14 | ) 15 | 16 | // ClientTraceInterceptor trace interceptor 17 | func ClientTraceInterceptor() interceptor.ClientInterceptor { 18 | return func(ctx context.Context, method, target string, req, resp interface{}, h interceptor.Invoker) error { 19 | md := metadata.ExtractClientMetadata(ctx) 20 | 21 | tr := otel.GetTracerProvider().Tracer(strace.TraceName) 22 | name, attrs := strace.BuildSpan(method, target) 23 | ctx, span := tr.Start(ctx, name, trace.WithAttributes(attrs...), trace.WithSpanKind(trace.SpanKindClient)) 24 | defer span.End() 25 | 26 | strace.Inject(ctx, otel.GetTextMapPropagator(), md) 27 | ctx = metadata.WithClientMetadata(ctx, md) 28 | 29 | err := h(ctx, req, resp) 30 | if err != nil { 31 | s, ok := srpcerr.FromError(err) 32 | if ok { 33 | span.SetStatus(codes.Error, s.GetMsg()) 34 | span.SetAttributes(strace.StatusCodeAttr(s.GetCode())) 35 | } else { 36 | span.SetStatus(codes.Error, err.Error()) 37 | } 38 | return err 39 | } 40 | 41 | span.SetAttributes(strace.StatusCodeAttr(0)) 42 | return nil 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /interceptor/clientinterceptor/traceinterceptor_test.go: -------------------------------------------------------------------------------- 1 | package clientinterceptor 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | strace "github.com/wsx864321/srpc/trace" 8 | ) 9 | 10 | func TestClientTraceInterceptor(t *testing.T) { 11 | strace.StartAgent() 12 | defer strace.StopAgent() 13 | 14 | ClientTraceInterceptor()(context.Background(), "srpc-test/helloworld", "127.0.0.1:7777", "", "", func(ctx context.Context, req, resp interface{}) error { 15 | return nil 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /interceptor/interceptor.go: -------------------------------------------------------------------------------- 1 | package interceptor 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | type ServerInterceptor func(ctx context.Context, req interface{}, h Handler) (interface{}, error) 8 | 9 | type Handler func(ctx context.Context, req interface{}) (interface{}, error) 10 | 11 | type ClientInterceptor func(ctx context.Context, method, target string, req, resp interface{}, h Invoker) error 12 | 13 | type Invoker func(ctx context.Context, req, resp interface{}) error 14 | 15 | // ServerIntercept server端拦截器 16 | func ServerIntercept(ctx context.Context, req interface{}, ceps []ServerInterceptor, h Handler) (interface{}, error) { 17 | if len(ceps) == 0 { 18 | return h(ctx, req) 19 | } 20 | return ceps[0](ctx, req, getHandler(0, ceps, h)) 21 | } 22 | 23 | func getHandler(cur int, ceps []ServerInterceptor, h Handler) Handler { 24 | if cur == len(ceps)-1 { 25 | return h 26 | } 27 | return func(ctx context.Context, req interface{}) (interface{}, error) { 28 | return ceps[cur+1](ctx, req, getHandler(cur+1, ceps, h)) 29 | } 30 | } 31 | 32 | // ClientIntercept client端拦截器 33 | func ClientIntercept(ctx context.Context, method, target string, req, resp interface{}, ceps []ClientInterceptor, i Invoker) error { 34 | if len(ceps) == 0 { 35 | return i(ctx, req, resp) 36 | } 37 | return ceps[0](ctx, method, target, req, resp, getInvoker(0, method, target, ceps, i)) 38 | } 39 | 40 | func getInvoker(cur int, method, target string, ceps []ClientInterceptor, i Invoker) Invoker { 41 | if cur == len(ceps)-1 { 42 | return i 43 | } 44 | return func(ctx context.Context, req, resp interface{}) error { 45 | return ceps[cur+1](ctx, method, target, req, resp, getInvoker(cur+1, method, target, ceps, i)) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /interceptor/interceptor_test.go: -------------------------------------------------------------------------------- 1 | package interceptor 2 | 3 | import "testing" 4 | 5 | func TestServerIntercept(t *testing.T) { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /interceptor/serverinterceptor/timeoutinterceptor.go: -------------------------------------------------------------------------------- 1 | package serverinterceptor 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "strconv" 7 | "time" 8 | 9 | "github.com/wsx864321/srpc/interceptor" 10 | "github.com/wsx864321/srpc/metadata" 11 | ) 12 | 13 | // ServerTimeoutInterceptor 服务端超时控制,与client进行配合做到级联超时控制 14 | func ServerTimeoutInterceptor() interceptor.ServerInterceptor { 15 | return func(ctx context.Context, req interface{}, h interceptor.Handler) (interface{}, error) { 16 | md := metadata.ExtractServerMetadata(ctx) 17 | if val, ok := md[metadata.SRPCTimeout]; ok { 18 | dur, _ := strconv.ParseInt(val, 10, 64) 19 | ctx, _ = context.WithTimeout(ctx, time.Nanosecond*time.Duration(dur)) 20 | } 21 | 22 | var ( 23 | finish = make(chan struct{}, 1) 24 | resp interface{} 25 | err error 26 | ) 27 | 28 | go func() { 29 | resp, err = h(ctx, req) 30 | 31 | finish <- struct{}{} 32 | }() 33 | 34 | // 执行业务逻辑后操作 35 | select { 36 | case <-finish: 37 | return resp, err 38 | case <-ctx.Done(): 39 | return nil, errors.New("server timeout") 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /interceptor/serverinterceptor/traceinterceptor.go: -------------------------------------------------------------------------------- 1 | package serverinterceptor 2 | 3 | import ( 4 | "context" 5 | 6 | srpcerr "github.com/wsx864321/srpc/err" 7 | "github.com/wsx864321/srpc/interceptor" 8 | "github.com/wsx864321/srpc/metadata" 9 | strace "github.com/wsx864321/srpc/trace" 10 | "go.opentelemetry.io/otel" 11 | "go.opentelemetry.io/otel/codes" 12 | "go.opentelemetry.io/otel/trace" 13 | ) 14 | 15 | func ServerTraceInterceptor() interceptor.ServerInterceptor { 16 | return func(ctx context.Context, req interface{}, h interceptor.Handler) (interface{}, error) { 17 | md := metadata.ExtractServerMetadata(ctx) 18 | 19 | spanCtx := strace.Extract(ctx, otel.GetTextMapPropagator(), md) 20 | tr := otel.Tracer(strace.TraceName) 21 | name, attrs := strace.BuildSpan(md[metadata.SPRCFullMethod], md[metadata.SRPCPeerAddr]) 22 | 23 | ctx, span := tr.Start(trace.ContextWithRemoteSpanContext(ctx, spanCtx), name, trace.WithSpanKind(trace.SpanKindServer), trace.WithAttributes(attrs...)) 24 | defer span.End() 25 | 26 | resp, err := h(ctx, req) 27 | if err != nil { 28 | s, ok := srpcerr.FromError(err) 29 | if ok { 30 | span.SetStatus(codes.Error, s.GetMsg()) 31 | span.SetAttributes(strace.StatusCodeAttr(s.GetCode())) 32 | } else { 33 | span.SetStatus(codes.Error, err.Error()) 34 | } 35 | return nil, err 36 | } 37 | 38 | return resp, nil 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lb/loadbalance.go: -------------------------------------------------------------------------------- 1 | package lb 2 | 3 | import "github.com/wsx864321/srpc/discov" 4 | 5 | type LoadBalance interface { 6 | Name() string 7 | Pick(*discov.Service) (*discov.Endpoint, error) 8 | } 9 | 10 | var lbMgr = map[string]LoadBalance{ 11 | LoadBalanceRandom: NewRandom(), 12 | } 13 | 14 | // RegisterLB 注册loadBalance 15 | func RegisterLB(lbName string, lb LoadBalance) { 16 | lbMgr[lbName] = lb 17 | } 18 | 19 | // GetLB 获取loadBalance方式 20 | func GetLB(lbName string) LoadBalance { 21 | return lbMgr[lbName] 22 | } 23 | -------------------------------------------------------------------------------- /lb/random.go: -------------------------------------------------------------------------------- 1 | package lb 2 | 3 | import ( 4 | "crypto/rand" 5 | "errors" 6 | "math/big" 7 | 8 | "github.com/wsx864321/srpc/discov" 9 | ) 10 | 11 | const LoadBalanceRandom = "random" 12 | 13 | type Random struct { 14 | } 15 | 16 | func NewRandom() LoadBalance { 17 | return &Random{} 18 | } 19 | 20 | func (r *Random) Name() string { 21 | return LoadBalanceRandom 22 | } 23 | 24 | func (r *Random) Pick(service *discov.Service) (*discov.Endpoint, error) { 25 | count := len(service.Endpoints) 26 | if count == 0 { 27 | return nil, errors.New("endpoint is empty") 28 | } 29 | 30 | n, _ := rand.Int(rand.Reader, big.NewInt(int64(count))) 31 | 32 | return service.Endpoints[n.Int64()], nil 33 | } 34 | -------------------------------------------------------------------------------- /logger/log.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "path" 8 | "runtime" 9 | ) 10 | 11 | type Log interface { 12 | Debugf(ctx context.Context, format string, a ...interface{}) 13 | Infof(ctx context.Context, format string, a ...interface{}) 14 | Warnf(ctx context.Context, format string, a ...interface{}) 15 | Errorf(ctx context.Context, format string, a ...interface{}) 16 | } 17 | 18 | type SweetLog struct { 19 | } 20 | 21 | func NewSweetLog() *SweetLog { 22 | return &SweetLog{} 23 | } 24 | 25 | func (s *SweetLog) Debugf(ctx context.Context, format string, a ...interface{}) { 26 | log.Printf("【DEBUG】"+s.caller()+" "+format, a...) 27 | } 28 | 29 | func (s *SweetLog) Infof(ctx context.Context, format string, a ...interface{}) { 30 | log.Printf("【INFO】"+s.caller()+" "+format, a...) 31 | } 32 | 33 | func (s *SweetLog) Warnf(ctx context.Context, format string, a ...interface{}) { 34 | log.Printf("【WARN】"+s.caller()+" "+format, a...) 35 | } 36 | 37 | func (s *SweetLog) Errorf(ctx context.Context, format string, a ...interface{}) { 38 | log.Printf("【ERROR】"+s.caller()+" "+format, a...) 39 | } 40 | 41 | func (s *SweetLog) caller() string { 42 | var ( 43 | pc uintptr 44 | file string 45 | lineNo int 46 | ok bool 47 | funcName string 48 | ) 49 | 50 | pc, file, lineNo, ok = runtime.Caller(2) 51 | if ok { 52 | funcName = runtime.FuncForPC(pc).Name() 53 | } 54 | 55 | return fmt.Sprintf("%s/%s:%d", path.Base(file), path.Base(funcName), lineNo) 56 | } 57 | -------------------------------------------------------------------------------- /logger/log_test.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func Test_getLineInfo(t *testing.T) { 9 | l := NewSweetLog() 10 | fmt.Println(l.caller()) 11 | } 12 | -------------------------------------------------------------------------------- /metadata/metadata.go: -------------------------------------------------------------------------------- 1 | package metadata 2 | 3 | import "context" 4 | 5 | const ( 6 | SRPCTimeout = "srpc-timeout" 7 | SRPCPeerAddr = "srpc-peer-addr" 8 | SPRCFullMethod = "srpc-full-method" 9 | ) 10 | 11 | type clientMD struct{} 12 | type serverMD struct{} 13 | 14 | type ClientMetadata map[string]string 15 | 16 | type ServerMetadata map[string]string 17 | 18 | // ExtractClientMetadata extract ClientMetadata from context 19 | func ExtractClientMetadata(ctx context.Context) ClientMetadata { 20 | if md, ok := ctx.Value(clientMD{}).(ClientMetadata); ok { 21 | return md 22 | } 23 | md := make(map[string]string) 24 | WithClientMetadata(ctx, md) 25 | return md 26 | } 27 | 28 | // WithClientMetadata creates a new context with the specified metadata 29 | func WithClientMetadata(ctx context.Context, metadata map[string]string) context.Context { 30 | return context.WithValue(ctx, clientMD{}, ClientMetadata(metadata)) 31 | } 32 | 33 | // ExtractServerMetadata extract ServerMetadata from context 34 | func ExtractServerMetadata(ctx context.Context) ServerMetadata { 35 | if md, ok := ctx.Value(serverMD{}).(ServerMetadata); ok { 36 | return md 37 | } 38 | md := make(map[string]string) 39 | WithServerMetadata(ctx, md) 40 | return md 41 | } 42 | 43 | // WithServerMetadata creates a new context with the specified metadata 44 | func WithServerMetadata(ctx context.Context, metadata map[string]string) context.Context { 45 | return context.WithValue(ctx, serverMD{}, ServerMetadata(metadata)) 46 | } 47 | -------------------------------------------------------------------------------- /metadata/metadata.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go v1.28.1 4 | // protoc v3.21.12 5 | // source: metadata.proto 6 | 7 | package metadata 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 Metadata struct { 24 | state protoimpl.MessageState 25 | sizeCache protoimpl.SizeCache 26 | unknownFields protoimpl.UnknownFields 27 | 28 | Data map[string]string `protobuf:"bytes,1,rep,name=data,proto3" json:"data,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` 29 | } 30 | 31 | func (x *Metadata) Reset() { 32 | *x = Metadata{} 33 | if protoimpl.UnsafeEnabled { 34 | mi := &file_metadata_proto_msgTypes[0] 35 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 36 | ms.StoreMessageInfo(mi) 37 | } 38 | } 39 | 40 | func (x *Metadata) String() string { 41 | return protoimpl.X.MessageStringOf(x) 42 | } 43 | 44 | func (*Metadata) ProtoMessage() {} 45 | 46 | func (x *Metadata) ProtoReflect() protoreflect.Message { 47 | mi := &file_metadata_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 Metadata.ProtoReflect.Descriptor instead. 59 | func (*Metadata) Descriptor() ([]byte, []int) { 60 | return file_metadata_proto_rawDescGZIP(), []int{0} 61 | } 62 | 63 | func (x *Metadata) GetData() map[string]string { 64 | if x != nil { 65 | return x.Data 66 | } 67 | return nil 68 | } 69 | 70 | var File_metadata_proto protoreflect.FileDescriptor 71 | 72 | var file_metadata_proto_rawDesc = []byte{ 73 | 0x0a, 0x0e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 74 | 0x22, 0x6c, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x27, 0x0a, 0x04, 75 | 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x4d, 0x65, 0x74, 76 | 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 77 | 0x04, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x37, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 78 | 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 79 | 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 80 | 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0d, 81 | 0x5a, 0x0b, 0x2e, 0x2f, 0x3b, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x62, 0x06, 0x70, 82 | 0x72, 0x6f, 0x74, 0x6f, 0x33, 83 | } 84 | 85 | var ( 86 | file_metadata_proto_rawDescOnce sync.Once 87 | file_metadata_proto_rawDescData = file_metadata_proto_rawDesc 88 | ) 89 | 90 | func file_metadata_proto_rawDescGZIP() []byte { 91 | file_metadata_proto_rawDescOnce.Do(func() { 92 | file_metadata_proto_rawDescData = protoimpl.X.CompressGZIP(file_metadata_proto_rawDescData) 93 | }) 94 | return file_metadata_proto_rawDescData 95 | } 96 | 97 | var file_metadata_proto_msgTypes = make([]protoimpl.MessageInfo, 2) 98 | var file_metadata_proto_goTypes = []interface{}{ 99 | (*Metadata)(nil), // 0: Metadata 100 | nil, // 1: Metadata.DataEntry 101 | } 102 | var file_metadata_proto_depIdxs = []int32{ 103 | 1, // 0: Metadata.data:type_name -> Metadata.DataEntry 104 | 1, // [1:1] is the sub-list for method output_type 105 | 1, // [1:1] is the sub-list for method input_type 106 | 1, // [1:1] is the sub-list for extension type_name 107 | 1, // [1:1] is the sub-list for extension extendee 108 | 0, // [0:1] is the sub-list for field type_name 109 | } 110 | 111 | func init() { file_metadata_proto_init() } 112 | func file_metadata_proto_init() { 113 | if File_metadata_proto != nil { 114 | return 115 | } 116 | if !protoimpl.UnsafeEnabled { 117 | file_metadata_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 118 | switch v := v.(*Metadata); i { 119 | case 0: 120 | return &v.state 121 | case 1: 122 | return &v.sizeCache 123 | case 2: 124 | return &v.unknownFields 125 | default: 126 | return nil 127 | } 128 | } 129 | } 130 | type x struct{} 131 | out := protoimpl.TypeBuilder{ 132 | File: protoimpl.DescBuilder{ 133 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 134 | RawDescriptor: file_metadata_proto_rawDesc, 135 | NumEnums: 0, 136 | NumMessages: 2, 137 | NumExtensions: 0, 138 | NumServices: 0, 139 | }, 140 | GoTypes: file_metadata_proto_goTypes, 141 | DependencyIndexes: file_metadata_proto_depIdxs, 142 | MessageInfos: file_metadata_proto_msgTypes, 143 | }.Build() 144 | File_metadata_proto = out.File 145 | file_metadata_proto_rawDesc = nil 146 | file_metadata_proto_goTypes = nil 147 | file_metadata_proto_depIdxs = nil 148 | } 149 | -------------------------------------------------------------------------------- /metadata/metadata.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "./;metadata"; 4 | 5 | message Metadata { 6 | map data = 1; 7 | } -------------------------------------------------------------------------------- /pool/channel.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "net" 8 | "sync" 9 | "time" 10 | //"reflect" 11 | ) 12 | 13 | var ( 14 | //ErrMaxActiveConnReached 连接池超限 15 | ErrMaxActiveConnReached = errors.New("MaxActiveConnReached") 16 | ) 17 | 18 | // channelPool 存放连接信息 19 | type channelPool struct { 20 | *Options 21 | 22 | mu sync.RWMutex 23 | conns chan *idleConn 24 | openingConns int 25 | } 26 | 27 | type idleConn struct { 28 | conn net.Conn 29 | t time.Time 30 | } 31 | 32 | // NewChannelPool 初始化连接 33 | func NewChannelPool(opts ...Option) (*channelPool, error) { 34 | opt := NewOptions(opts...) 35 | c := &channelPool{ 36 | Options: opt, 37 | mu: sync.RWMutex{}, 38 | conns: make(chan *idleConn, opt.maxCap), 39 | openingConns: opt.initialCap, 40 | } 41 | 42 | for i := 0; i < opt.initialCap; i++ { 43 | if c.factory == nil { 44 | fmt.Println(0) 45 | fmt.Println(opts) 46 | fmt.Println(NewOptions(opts...)) 47 | } 48 | conn, err := c.factory(opt.network, opt.address, opt.dailTimeout) 49 | if err != nil { 50 | c.release() 51 | return nil, fmt.Errorf("factory is not able to fill the pool: %s", err) 52 | } 53 | c.conns <- &idleConn{conn: conn, t: time.Now()} 54 | } 55 | 56 | return c, nil 57 | } 58 | 59 | // getConns 获取所有连接 60 | func (c *channelPool) getConns() chan *idleConn { 61 | return c.conns 62 | } 63 | 64 | // Get 从pool中取一个连接 todo 增加超时控制 65 | func (c *channelPool) get(ctx context.Context, network, address string) (net.Conn, error) { 66 | conns := c.getConns() 67 | if conns == nil { 68 | return nil, ErrClosed 69 | } 70 | for { 71 | select { 72 | case <-ctx.Done(): 73 | return nil, ErrGetTimeout 74 | case wrapConn := <-conns: 75 | if wrapConn == nil { 76 | return nil, ErrClosed 77 | } 78 | //判断是否超时,超时则丢弃 79 | if timeout := c.idleTimeout; timeout > 0 { 80 | if wrapConn.t.Add(timeout).Before(time.Now()) { 81 | //丢弃并关闭该连接 82 | c.close(wrapConn.conn) 83 | continue 84 | } 85 | } 86 | //判断是否失效,失效则丢弃,如果用户没有设定 ping 方法,就不检查 87 | if c.pingFunc != nil { 88 | if err := c.ping(wrapConn.conn); err != nil { 89 | c.close(wrapConn.conn) 90 | continue 91 | } 92 | } 93 | return wrapConn.conn, nil 94 | default: 95 | 96 | if c.openingConns >= c.maxCap { 97 | for { 98 | select { 99 | case <-ctx.Done(): 100 | return nil, ErrGetTimeout 101 | case wrapConn := <-conns: 102 | if wrapConn == nil { 103 | return nil, ErrClosed 104 | } 105 | //判断是否超时,超时则丢弃 106 | if timeout := c.idleTimeout; timeout > 0 { 107 | if wrapConn.t.Add(timeout).Before(time.Now()) { 108 | //丢弃并关闭该连接 109 | c.close(wrapConn.conn) 110 | continue 111 | } 112 | } 113 | //判断是否失效,失效则丢弃,如果用户没有设定 ping 方法,就不检查 114 | if c.pingFunc != nil { 115 | if err := c.ping(wrapConn.conn); err != nil { 116 | c.close(wrapConn.conn) 117 | continue 118 | } 119 | } 120 | return wrapConn.conn, nil 121 | } 122 | } 123 | } 124 | 125 | conn, err := c.factory(network, address, c.dailTimeout) 126 | if err != nil { 127 | return nil, err 128 | } 129 | 130 | c.mu.Lock() 131 | defer c.mu.Unlock() 132 | 133 | c.openingConns++ 134 | return conn, nil 135 | } 136 | } 137 | } 138 | 139 | // Put 将连接放回pool中 140 | func (c *channelPool) put(conn net.Conn) error { 141 | if conn == nil { 142 | return errors.New("connection is nil. rejecting") 143 | } 144 | 145 | if c.conns == nil { 146 | return c.close(conn) 147 | } 148 | 149 | select { 150 | case c.conns <- &idleConn{conn: conn, t: time.Now()}: 151 | return nil 152 | default: 153 | //连接池已满,直接关闭该连接 154 | return c.close(conn) 155 | } 156 | 157 | } 158 | 159 | // Close 关闭单条连接 160 | func (c *channelPool) close(conn net.Conn) error { 161 | if conn == nil { 162 | return errors.New("connection is nil. rejecting") 163 | } 164 | 165 | if c.closeFunc == nil { 166 | return nil 167 | } 168 | c.mu.Lock() 169 | c.openingConns-- 170 | c.mu.Unlock() 171 | 172 | return c.close(conn) 173 | } 174 | 175 | // Ping 检查单条连接是否有效 176 | func (c *channelPool) ping(conn net.Conn) error { 177 | if conn == nil { 178 | return errors.New("connection is nil. rejecting") 179 | } 180 | return c.pingFunc(conn) 181 | } 182 | 183 | // release 释放连接池中所有连接 184 | func (c *channelPool) release() { 185 | c.mu.Lock() 186 | conns := c.conns 187 | c.conns = nil 188 | c.factory = nil 189 | c.pingFunc = nil 190 | closeFun := c.close 191 | c.closeFunc = nil 192 | c.mu.Unlock() 193 | 194 | if conns == nil { 195 | return 196 | } 197 | 198 | close(conns) 199 | for wrapConn := range conns { 200 | //log.Printf("Type %v\n",reflect.TypeOf(wrapConn.conn)) 201 | closeFun(wrapConn.conn) 202 | } 203 | } 204 | 205 | // Len 连接池中已有的连接 206 | func (c *channelPool) len() int { 207 | return len(c.getConns()) 208 | } 209 | -------------------------------------------------------------------------------- /pool/channel_rpc_test.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "context" 5 | "math/rand" 6 | "net" 7 | "net/http" 8 | "net/rpc" 9 | "sync" 10 | "testing" 11 | "time" 12 | ) 13 | 14 | var ( 15 | InitialCap = 5 16 | MaxIdleCap = 10 17 | MaximumCap = 100 18 | network = "tcp" 19 | address = "127.0.0.1:7777" 20 | //factory = func() (interface{}, error) { return net.Dial(network, address) } 21 | factory = func(network, address string, timeout time.Duration) (net.Conn, error) { 22 | return net.Dial("tcp", address) 23 | } 24 | closeFac = func(v net.Conn) error { 25 | return v.Close() 26 | } 27 | ) 28 | 29 | func init() { 30 | // used for factory function 31 | go rpcServer() 32 | time.Sleep(time.Millisecond * 300) // wait until tcp server has been settled 33 | 34 | rand.Seed(time.Now().UTC().UnixNano()) 35 | } 36 | 37 | func TestNew(t *testing.T) { 38 | p, err := newChannelPool() 39 | defer p.release() 40 | if err != nil { 41 | t.Errorf("New error: %s", err) 42 | } 43 | } 44 | func TestPool_Get_Impl(t *testing.T) { 45 | p, _ := newChannelPool() 46 | defer p.release() 47 | 48 | conn, err := p.get(context.Background(), network, address) 49 | if err != nil { 50 | t.Errorf("Get error: %s", err) 51 | } 52 | 53 | p.put(conn) 54 | } 55 | 56 | func TestPool_Get(t *testing.T) { 57 | p, _ := newChannelPool() 58 | defer p.release() 59 | 60 | _, err := p.get(context.Background(), network, address) 61 | if err != nil { 62 | t.Errorf("Get error: %s", err) 63 | } 64 | 65 | // after one get, current capacity should be lowered by one. 66 | if p.len() != (InitialCap - 1) { 67 | t.Errorf("Get error. Expecting %d, got %d", 68 | (InitialCap - 1), p.len()) 69 | } 70 | 71 | // get them all 72 | var wg sync.WaitGroup 73 | for i := 0; i < (MaximumCap - 1); i++ { 74 | wg.Add(1) 75 | go func() { 76 | defer wg.Done() 77 | _, err := p.get(context.Background(), network, address) 78 | if err != nil { 79 | t.Errorf("Get error: %s", err) 80 | } 81 | }() 82 | } 83 | wg.Wait() 84 | 85 | if p.len() != 0 { 86 | t.Errorf("Get error. Expecting %d, got %d", 87 | (InitialCap - 1), p.len()) 88 | } 89 | 90 | _, err = p.get(context.Background(), network, address) 91 | if err != ErrMaxActiveConnReached { 92 | t.Errorf("Get error: %s", err) 93 | } 94 | 95 | } 96 | 97 | func TestPool_Put(t *testing.T) { 98 | p, err := NewChannelPool(WithInitialCap(InitialCap), WithMaxCap(MaximumCap), WithFactory(factory), WithClose(closeFac), WithIdleTimeout(time.Second*20)) 99 | if err != nil { 100 | t.Fatal(err) 101 | } 102 | defer p.release() 103 | 104 | // get/create from the pool 105 | conns := make([]net.Conn, MaximumCap) 106 | for i := 0; i < MaximumCap; i++ { 107 | conn, _ := p.get(context.Background(), network, address) 108 | conns[i] = conn 109 | } 110 | 111 | // now put them all back 112 | for _, conn := range conns { 113 | p.put(conn) 114 | } 115 | 116 | if p.len() != MaximumCap { 117 | t.Errorf("Put error len. Expecting %d, got %d", 118 | 1, p.len()) 119 | } 120 | 121 | p.release() // close pool 122 | 123 | } 124 | 125 | func TestPool_UsedCapacity(t *testing.T) { 126 | p, _ := newChannelPool() 127 | defer p.release() 128 | 129 | if p.len() != InitialCap { 130 | t.Errorf("InitialCap error. Expecting %d, got %d", 131 | InitialCap, p.len()) 132 | } 133 | } 134 | 135 | func TestPool_Close(t *testing.T) { 136 | p, _ := newChannelPool() 137 | 138 | // now close it and test all cases we are expecting. 139 | p.release() 140 | 141 | c := p 142 | 143 | if c.conns != nil { 144 | t.Errorf("Close error, conns channel should be nil") 145 | } 146 | 147 | if c.factory != nil { 148 | t.Errorf("Close error, factory should be nil") 149 | } 150 | 151 | _, err := p.get(context.Background(), network, address) 152 | if err == nil { 153 | t.Errorf("Close error, get conn should return an error") 154 | } 155 | 156 | if p.len() != 0 { 157 | t.Errorf("Close error used capacity. Expecting 0, got %d", p.len()) 158 | } 159 | } 160 | 161 | func TestPoolConcurrent(t *testing.T) { 162 | p, _ := newChannelPool() 163 | pipe := make(chan net.Conn, 0) 164 | 165 | go func() { 166 | p.release() 167 | }() 168 | 169 | for i := 0; i < MaximumCap; i++ { 170 | go func() { 171 | conn, _ := p.get(context.Background(), network, address) 172 | 173 | pipe <- conn 174 | }() 175 | 176 | go func() { 177 | conn := <-pipe 178 | if conn == nil { 179 | return 180 | } 181 | p.put(conn) 182 | }() 183 | } 184 | } 185 | 186 | func TestPoolConcurrent2(t *testing.T) { 187 | //p, _ := NewChannelPool(0, 30, factory) 188 | p, _ := newChannelPool() 189 | 190 | var wg sync.WaitGroup 191 | 192 | go func() { 193 | for i := 0; i < 10; i++ { 194 | wg.Add(1) 195 | go func(i int) { 196 | conn, _ := p.get(context.Background(), network, address) 197 | time.Sleep(time.Millisecond * time.Duration(rand.Intn(100))) 198 | p.close(conn) 199 | wg.Done() 200 | }(i) 201 | } 202 | }() 203 | 204 | for i := 0; i < 10; i++ { 205 | wg.Add(1) 206 | go func(i int) { 207 | conn, _ := p.get(context.Background(), network, address) 208 | time.Sleep(time.Millisecond * time.Duration(rand.Intn(100))) 209 | p.close(conn) 210 | wg.Done() 211 | }(i) 212 | } 213 | 214 | wg.Wait() 215 | } 216 | 217 | func newChannelPool() (*channelPool, error) { 218 | return NewChannelPool(WithInitialCap(InitialCap), WithMaxCap(MaximumCap), WithFactory(factory), WithClose(closeFac), WithIdleTimeout(time.Second*20)) 219 | } 220 | 221 | func rpcServer() { 222 | arith := new(Arith) 223 | rpc.Register(arith) 224 | rpc.HandleHTTP() 225 | 226 | l, e := net.Listen("tcp", address) 227 | if e != nil { 228 | panic(e) 229 | } 230 | go http.Serve(l, nil) 231 | } 232 | 233 | type Args struct { 234 | A, B int 235 | } 236 | 237 | type Arith int 238 | 239 | func (t *Arith) Multiply(args *Args, reply *int) error { 240 | *reply = args.A * args.B 241 | return nil 242 | } 243 | -------------------------------------------------------------------------------- /pool/options.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "net" 5 | "time" 6 | ) 7 | 8 | var defaultOptions = &Options{ 9 | initialCap: 1, 10 | maxCap: 100, 11 | factory: func(network, address string, timeout time.Duration) (net.Conn, error) { 12 | return net.DialTimeout(network, address, timeout) 13 | }, 14 | closeFunc: func(conn net.Conn) error { 15 | return conn.Close() 16 | }, 17 | pingFunc: nil, 18 | idleTimeout: 1 * time.Minute, 19 | network: "tcp", 20 | address: "127.0.0.1:7777", 21 | dailTimeout: 1000 * time.Millisecond, 22 | } 23 | 24 | type Options struct { 25 | //连接池中拥有的最小连接数 26 | initialCap int 27 | //最大并发存活连接数 28 | maxCap int 29 | //生成连接的方法 30 | factory func(network, address string, timeout time.Duration) (net.Conn, error) 31 | //关闭连接的方法 32 | closeFunc func(conn net.Conn) error 33 | //检查连接是否有效的方法 34 | pingFunc func(conn net.Conn) error 35 | //连接最大空闲时间,超过该事件则将失效 36 | idleTimeout time.Duration 37 | // network eg:tcp udp 38 | network string 39 | // ip+port 0.0.0.0:9999 40 | address string 41 | // dailTimeout 42 | dailTimeout time.Duration 43 | } 44 | 45 | type Option func(opts *Options) 46 | 47 | func WithInitialCap(cap int) Option { 48 | return func(opts *Options) { 49 | opts.initialCap = cap 50 | } 51 | } 52 | 53 | func WithMaxCap(cap int) Option { 54 | return func(opts *Options) { 55 | opts.maxCap = cap 56 | } 57 | } 58 | 59 | func WithFactory(factory func(network, address string, timeout time.Duration) (net.Conn, error)) Option { 60 | return func(opts *Options) { 61 | opts.factory = factory 62 | } 63 | } 64 | 65 | func WithClose(close func(conn net.Conn) error) Option { 66 | return func(opts *Options) { 67 | opts.closeFunc = close 68 | } 69 | } 70 | 71 | func WithPing(ping func(conn net.Conn) error) Option { 72 | return func(opts *Options) { 73 | opts.pingFunc = ping 74 | } 75 | } 76 | 77 | func WithIdleTimeout(idleTimeout time.Duration) Option { 78 | return func(opts *Options) { 79 | opts.idleTimeout = idleTimeout 80 | } 81 | } 82 | 83 | func WithNetwork(network string) Option { 84 | return func(opts *Options) { 85 | opts.network = network 86 | } 87 | } 88 | 89 | func WithAddress(addr string) Option { 90 | return func(opts *Options) { 91 | opts.address = addr 92 | } 93 | } 94 | 95 | func WithDailTimeout(timeout time.Duration) Option { 96 | return func(opts *Options) { 97 | opts.dailTimeout = timeout 98 | } 99 | } 100 | 101 | func NewOptions(opts ...Option) *Options { 102 | opt := defaultOptions 103 | for _, o := range opts { 104 | o(opt) 105 | } 106 | 107 | return opt 108 | } 109 | -------------------------------------------------------------------------------- /pool/pool.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "net" 8 | "sync" 9 | ) 10 | 11 | var ( 12 | //ErrClosed 连接池已经关闭Error 13 | ErrClosed = errors.New("pool is closed") 14 | ErrGetTimeout = errors.New("poo get client timeout") 15 | ) 16 | 17 | // Pool 基本方法 18 | //type Pool interface { 19 | // Get(address, network string) (net.Conn, error) 20 | // 21 | // Put(conn net.Conn) error 22 | // 23 | // Close(conn net.Conn) error 24 | // 25 | // Release() 26 | // 27 | // Len() int 28 | //} 29 | 30 | type Pool struct { 31 | opts *Options 32 | conns *sync.Map 33 | mu *sync.Mutex 34 | } 35 | 36 | // NewPool todo 增加异步client链接检查,防止下游节点因为服务下线而连接还保存在map中造成缓慢的内存泄露和fd的浪费 37 | func NewPool(opts ...Option) *Pool { 38 | opt := NewOptions(opts...) 39 | if !(opt.initialCap <= opt.maxCap) { 40 | panic("invalid capacity settings") 41 | } 42 | if opt.factory == nil { 43 | panic("invalid factory func settings") 44 | } 45 | if opt.closeFunc == nil { 46 | panic("invalid close func settings") 47 | } 48 | 49 | return &Pool{ 50 | opts: opt, 51 | conns: &sync.Map{}, 52 | mu: &sync.Mutex{}, 53 | } 54 | } 55 | 56 | // Get ... 57 | func (p *Pool) Get(ctx context.Context, network, address string) (net.Conn, error) { 58 | if value, ok := p.conns.Load(p.getKey(network, address)); ok { 59 | if cp, ok := value.(*channelPool); ok { 60 | conn, err := cp.get(ctx, network, address) 61 | return conn, err 62 | } 63 | } 64 | 65 | // check - lock -check 单例 66 | p.mu.Lock() 67 | defer p.mu.Unlock() 68 | if value, ok := p.conns.Load(p.getKey(network, address)); ok { 69 | if cp, ok := value.(*channelPool); ok { 70 | conn, err := cp.get(ctx, network, address) 71 | return conn, err 72 | } 73 | } 74 | 75 | cp, err := NewChannelPool( 76 | WithInitialCap(p.opts.initialCap), 77 | WithMaxCap(p.opts.maxCap), 78 | WithFactory(p.opts.factory), 79 | WithClose(p.opts.closeFunc), 80 | WithPing(p.opts.pingFunc), 81 | WithIdleTimeout(p.opts.idleTimeout), 82 | WithNetwork(network), 83 | WithAddress(address), 84 | WithDailTimeout(p.opts.dailTimeout), 85 | ) 86 | if err != nil { 87 | return nil, err 88 | } 89 | 90 | p.conns.Store(p.getKey(network, address), cp) 91 | 92 | return cp.get(ctx, network, address) 93 | } 94 | 95 | func (p *Pool) Put(network, address string, conn net.Conn) { 96 | if value, ok := p.conns.Load(p.getKey(network, address)); ok { 97 | if cp, ok := value.(*channelPool); ok { 98 | cp.put(conn) 99 | } 100 | } 101 | } 102 | 103 | func (p *Pool) CloseAll() { 104 | p.conns.Range(func(key, value interface{}) bool { 105 | if cp, ok := value.(*channelPool); ok { 106 | cp.release() 107 | } 108 | 109 | return true 110 | }) 111 | } 112 | 113 | func (p *Pool) getKey(network, address string) string { 114 | return fmt.Sprintf("%s://%s", network, address) 115 | } 116 | -------------------------------------------------------------------------------- /server/options.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/wsx864321/srpc/logger" 7 | 8 | "github.com/wsx864321/srpc/discov/etcd" 9 | 10 | "github.com/wsx864321/srpc/discov" 11 | 12 | "github.com/wsx864321/srpc/codec/serialize" 13 | "github.com/wsx864321/srpc/transport" 14 | ) 15 | 16 | var defaultOptions = &Options{ 17 | ip: "0.0.0.0", 18 | port: 9557, 19 | network: transport.NetworkTCP, 20 | serialize: serialize.SerializeTypeJson, 21 | timeout: 5 * time.Second, 22 | writeTimeout: 1 * time.Second, 23 | discovery: etcd.NewETCDRegister(), 24 | logger: logger.NewSweetLog(), 25 | } 26 | 27 | type Options struct { 28 | ip string 29 | port int 30 | network transport.Transport 31 | serialize serialize.SerializeType 32 | timeout time.Duration 33 | writeTimeout time.Duration 34 | discovery discov.Discovery 35 | logger logger.Log 36 | } 37 | 38 | type Option func(opt *Options) 39 | 40 | func WithIP(ip string) Option { 41 | return func(opt *Options) { 42 | opt.ip = ip 43 | } 44 | } 45 | 46 | func WithPort(port int) Option { 47 | return func(opt *Options) { 48 | opt.port = port 49 | } 50 | } 51 | 52 | func WithNetWork(network transport.Transport) Option { 53 | return func(opt *Options) { 54 | opt.network = network 55 | } 56 | } 57 | 58 | func WithSerialize(serializeType serialize.SerializeType) Option { 59 | return func(opt *Options) { 60 | opt.serialize = serializeType 61 | } 62 | } 63 | 64 | func WithTimeOut(duration time.Duration) Option { 65 | return func(opt *Options) { 66 | opt.timeout = duration 67 | } 68 | } 69 | 70 | func WithWriteTimeout(duration time.Duration) Option { 71 | return func(opt *Options) { 72 | opt.writeTimeout = duration 73 | } 74 | } 75 | 76 | func WithDiscovery(discovery discov.Discovery) Option { 77 | return func(opt *Options) { 78 | opt.discovery = discovery 79 | } 80 | } 81 | 82 | func WithLogger(log logger.Log) Option { 83 | return func(opt *Options) { 84 | opt.logger = log 85 | } 86 | } 87 | 88 | // NewOptions 初始化option 89 | func NewOptions(opts ...Option) *Options { 90 | opt := defaultOptions 91 | for _, fn := range opts { 92 | fn(opt) 93 | } 94 | 95 | return opt 96 | } 97 | -------------------------------------------------------------------------------- /server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net" 7 | "os" 8 | "os/signal" 9 | "reflect" 10 | "runtime" 11 | "syscall" 12 | "time" 13 | 14 | "github.com/wsx864321/srpc/metadata" 15 | 16 | "github.com/gogo/protobuf/proto" 17 | "github.com/wsx864321/srpc/codec" 18 | "github.com/wsx864321/srpc/codec/serialize" 19 | "github.com/wsx864321/srpc/discov" 20 | srpcerr "github.com/wsx864321/srpc/err" 21 | "github.com/wsx864321/srpc/interceptor" 22 | "github.com/wsx864321/srpc/transport" 23 | "github.com/wsx864321/srpc/util" 24 | ) 25 | 26 | type handler func(ctx context.Context, body []byte) (interface{}, error) 27 | 28 | type Server interface { 29 | Start() 30 | Stop() 31 | RegisterService(serName string, srv interface{}, interceptors ...interceptor.ServerInterceptor) 32 | } 33 | 34 | type server struct { 35 | opts *Options 36 | codec *codec.Codec 37 | ctx context.Context // Each service is managed in one context 38 | cancel context.CancelFunc // controller of context 39 | serviceMap map[string]*service 40 | } 41 | 42 | type service struct { 43 | name string 44 | methods map[string]handler 45 | } 46 | 47 | // NewServer 生成一个server 48 | func NewServer(opts ...Option) Server { 49 | ctx, cancel := context.WithCancel(context.Background()) 50 | return &server{ 51 | opts: NewOptions(opts...), 52 | codec: codec.NewCodec(), 53 | ctx: ctx, 54 | cancel: cancel, 55 | serviceMap: make(map[string]*service), 56 | } 57 | } 58 | 59 | // RegisterService 注册服务 60 | func (s *server) RegisterService(serName string, srv interface{}, interceptors ...interceptor.ServerInterceptor) { 61 | svrType := reflect.TypeOf(srv) 62 | svrValue := reflect.ValueOf(srv) 63 | 64 | methods := make(map[string]handler) 65 | for i := 0; i < svrType.NumMethod(); i++ { 66 | method := svrType.Method(i) 67 | if err := s.checkMethod(method.Name, method.Type); err != nil { 68 | panic(err) 69 | } 70 | 71 | methodHandler := func(ctx context.Context, body []byte) (interface{}, error) { 72 | req := reflect.New(method.Type.In(2).Elem()).Interface() 73 | if err := serialize.GetSerialize(s.opts.serialize).Unmarshal(body, req); err != nil { 74 | return nil, err 75 | } 76 | 77 | h := func(ctx context.Context, req interface{}) (interface{}, error) { 78 | resp := method.Func.Call([]reflect.Value{svrValue, reflect.ValueOf(ctx), reflect.ValueOf(req)}) 79 | errInterface := resp[1].Interface() 80 | if errInterface != nil { 81 | return resp[0].Interface(), errInterface.(error) 82 | } 83 | return resp[0].Interface(), nil 84 | 85 | } 86 | 87 | return interceptor.ServerIntercept(ctx, req, interceptors, h) 88 | } 89 | methods[method.Name] = methodHandler 90 | } 91 | 92 | s.serviceMap[serName] = &service{ 93 | serName, 94 | methods, 95 | } 96 | } 97 | 98 | // checkMethod 99 | func (s *server) checkMethod(methodName string, methodType reflect.Type) error { 100 | if methodType.NumIn() != 3 { 101 | return fmt.Errorf("method %s invalid, the number of params != 2", methodName) 102 | } 103 | 104 | if methodType.NumOut() != 2 { 105 | return fmt.Errorf("method %s invalid, the number of params != 2", methodName) 106 | } 107 | 108 | var ctx *context.Context 109 | if !methodType.In(1).Implements(reflect.TypeOf(ctx).Elem()) { 110 | return fmt.Errorf("method %s invalid, first param is not context", methodName) 111 | } 112 | 113 | if s.opts.serialize == serialize.SerializeTypeProto { 114 | var p *proto.Message 115 | if !methodType.In(2).Implements(reflect.TypeOf(p).Elem()) { 116 | return fmt.Errorf("method %s invalid, second param is not proto.Message", methodName) 117 | } 118 | } else { 119 | if methodType.In(2).Kind() != reflect.Ptr { 120 | return fmt.Errorf("method %s invalid, second param is not ptr", methodName) 121 | } 122 | } 123 | 124 | if s.opts.serialize == serialize.SerializeTypeProto { 125 | var p *proto.Message 126 | if !methodType.Out(0).Implements(reflect.TypeOf(p).Elem()) { 127 | return fmt.Errorf("method %s invalid, second param is not proto.Message", methodName) 128 | } 129 | } else { 130 | if methodType.Out(0).Kind() != reflect.Ptr { 131 | return fmt.Errorf("method %s invalid, first reply type is not a pointer", methodName) 132 | } 133 | } 134 | 135 | var err *error 136 | if !methodType.Out(1).Implements(reflect.TypeOf(err).Elem()) { 137 | return fmt.Errorf("method %s invalid, second reply type is not error", methodName) 138 | } 139 | 140 | return nil 141 | } 142 | 143 | // Start 启动server 144 | func (s *server) Start() { 145 | // 获取listener 146 | serverTransport := transport.GetTransport(s.opts.network) 147 | ln, err := serverTransport.Listen(fmt.Sprintf("%v:%v", s.opts.ip, s.opts.port)) 148 | if err != nil { 149 | panic(err) 150 | } 151 | 152 | s.opts.logger.Infof(context.TODO(), "%s server start at %s", s.opts.network, fmt.Sprintf("%v:%v", s.opts.ip, s.opts.port)) 153 | 154 | // accept请求 155 | go s.run(ln) 156 | 157 | // 等待100ms,让服务先接收请求 158 | time.Sleep(100 * time.Millisecond) 159 | 160 | // 注册服务 161 | for name := range s.serviceMap { 162 | s.opts.discovery.Register(context.TODO(), &discov.Service{ 163 | Name: name, 164 | Endpoints: []*discov.Endpoint{ 165 | { 166 | ServiceName: name, 167 | IP: s.opts.ip, 168 | Port: s.opts.port, 169 | Network: string(s.opts.network), 170 | Serialize: string(s.opts.serialize), 171 | Enable: true, 172 | }, 173 | }, 174 | }) 175 | } 176 | 177 | c := make(chan os.Signal, 1) 178 | signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) 179 | for { 180 | sig := <-c 181 | switch sig { 182 | case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT: 183 | s.Stop() 184 | time.Sleep(time.Second) 185 | return 186 | case syscall.SIGHUP: 187 | default: 188 | return 189 | } 190 | } 191 | } 192 | 193 | func (s *server) Stop() { 194 | // 服务取消注册 195 | for name := range s.serviceMap { 196 | s.opts.discovery.UnRegister(context.TODO(), &discov.Service{ 197 | Name: name, 198 | Endpoints: []*discov.Endpoint{ 199 | { 200 | ServiceName: name, 201 | IP: s.opts.ip, 202 | Port: s.opts.port, 203 | Enable: true, 204 | }, 205 | }, 206 | }) 207 | } 208 | 209 | s.cancel() 210 | } 211 | 212 | func (s *server) run(ln net.Listener) { 213 | defer func() { 214 | if r := recover(); r != nil { 215 | stack := make([]byte, 4096) 216 | stack = stack[:runtime.Stack(stack, false)] 217 | s.opts.logger.Errorf(context.TODO(), "err:%v\n.stack:%v", r, string(stack)) 218 | } 219 | }() 220 | 221 | for { 222 | select { 223 | case <-s.ctx.Done(): 224 | return 225 | default: 226 | accept, err := ln.Accept() 227 | if err != nil { 228 | s.opts.logger.Errorf(s.ctx, "err:%v", err) 229 | continue 230 | } 231 | 232 | // 开启一个协程处理请求 233 | go s.handleConn(s.ctx, accept) 234 | } 235 | 236 | } 237 | 238 | } 239 | 240 | // handleConn logic 241 | func (s *server) handleConn(pCtx context.Context, conn net.Conn) { 242 | defer func() { 243 | if r := recover(); r != nil { 244 | stack := make([]byte, 4096) 245 | stack = stack[:runtime.Stack(stack, false)] 246 | s.opts.logger.Errorf(context.TODO(), "\nerr:%v\nstack:%v", r, string(stack)) 247 | } 248 | }() 249 | 250 | for { 251 | select { 252 | case <-pCtx.Done(): 253 | return 254 | default: 255 | 256 | } 257 | 258 | if err := s.process(conn); err != nil { 259 | return 260 | } 261 | } 262 | 263 | } 264 | 265 | func (s *server) process(conn net.Conn) error { 266 | //1.提取数据 267 | msg, err := s.extractMessage(conn) 268 | // 这里为什么不用io.EOF 可以见:https://blog.csdn.net/aixinaxc/article/details/89282364 269 | if err != nil { 270 | return err 271 | } 272 | 273 | // 2.提取metadata 274 | var metaData metadata.Metadata 275 | if len(msg.MetaData) != 0 { 276 | if err = serialize.GetSerialize(s.opts.serialize).Unmarshal(msg.MetaData, &metaData); err != nil { 277 | s.wireErr(context.TODO(), conn, srpcerr.NewError(srpcerr.UnKnowErr, err.Error())) 278 | return nil 279 | } 280 | } 281 | ctx := metadata.WithServerMetadata(context.Background(), metaData.Data) 282 | 283 | //3.找到注册的服务和方法 284 | srv, ok := s.serviceMap[msg.ServiceName] 285 | if !ok { 286 | s.wireErr(ctx, conn, srpcerr.ServiceNotExistErr) 287 | return nil 288 | } 289 | 290 | methodHandler, ok := srv.methods[msg.ServiceMethod] 291 | if !ok { 292 | s.wireErr(ctx, conn, srpcerr.MethodNotExistErr) 293 | return nil 294 | } 295 | 296 | // 3.执行具体方法(包括注册的中间件) 297 | if s.opts.timeout > 0 { 298 | var cancel context.CancelFunc 299 | // 超时控制统一在timeoutInterceptor中间件中执行 300 | ctx, cancel = context.WithTimeout(ctx, s.opts.timeout) 301 | defer cancel() 302 | } 303 | resp, err := methodHandler(ctx, msg.Payload) 304 | 305 | // 4.对client发送返回数据 306 | if err != nil { 307 | if err, ok := err.(*srpcerr.Error); ok { 308 | s.wireErr(ctx, conn, err) 309 | return nil 310 | } 311 | 312 | s.wireErr(ctx, conn, srpcerr.NewError(srpcerr.UnKnowErr, err.Error())) 313 | return nil 314 | } 315 | 316 | raw, _ := serialize.GetSerialize(s.opts.serialize).Marshal(resp) 317 | raw, _ = serialize.GetSerialize(s.opts.serialize).Marshal(srpcerr.OkErr.WithData(raw)) 318 | if s.opts.writeTimeout > 0 { 319 | if err = conn.SetWriteDeadline(time.Now().Add(s.opts.writeTimeout)); err != nil { 320 | resp, _ := serialize.GetSerialize(s.opts.serialize).Marshal(err) 321 | raw, err = s.codec.Encode(codec.GeneralMsgType, codec.CompressTypeNot, uint64(time.Now().Unix()), []byte(""), []byte(""), []byte(""), resp) 322 | if err != nil { 323 | s.opts.logger.Errorf(ctx, "write error:%v", err.Error()) 324 | return nil 325 | } 326 | if err = util.Write(conn, raw); err != nil { 327 | s.opts.logger.Errorf(ctx, "write error:%v", err.Error()) 328 | return nil 329 | } 330 | } 331 | } 332 | 333 | raw, err = s.codec.Encode(codec.GeneralMsgType, codec.CompressTypeNot, uint64(time.Now().Unix()), []byte(""), []byte(""), []byte(""), raw) 334 | if err = util.Write(conn, raw); err != nil { 335 | s.opts.logger.Errorf(ctx, "write error:%v", err.Error()) 336 | } 337 | 338 | return nil 339 | } 340 | 341 | // extractMessage 提取message内容 342 | func (s *server) extractMessage(conn net.Conn) (*codec.Message, error) { 343 | // 1.读取头部内容 344 | headerData := make([]byte, s.codec.GetHeaderLength()) 345 | if err := util.ReadFixData(conn, headerData); err != nil { 346 | return nil, err 347 | } 348 | 349 | header, err := s.codec.DecodeHeader(headerData) 350 | if err != nil { 351 | return nil, err 352 | } 353 | 354 | // 2.读取message内容 355 | body := make([]byte, s.codec.GetBodyLength(header)) 356 | if err = util.ReadFixData(conn, body); err != nil { 357 | return nil, err 358 | } 359 | 360 | return s.codec.DecodeBody(header, body) 361 | } 362 | 363 | func (s *server) wireErr(ctx context.Context, conn net.Conn, err *srpcerr.Error) { 364 | if s.opts.writeTimeout > 0 { 365 | if e := conn.SetWriteDeadline(time.Now().Add(s.opts.writeTimeout)); e != nil { 366 | s.opts.logger.Errorf(context.TODO(), "SetWriteDeadline error:%v", err.Error()) 367 | resp, _ := serialize.GetSerialize(s.opts.serialize).Marshal(err) 368 | raw, e := s.codec.Encode(codec.GeneralMsgType, codec.CompressTypeNot, uint64(time.Now().Unix()), []byte(""), []byte(""), []byte(""), resp) 369 | if e != nil { 370 | s.opts.logger.Errorf(ctx, "write error:%v", e.Error()) 371 | return 372 | } 373 | 374 | if e = util.Write(conn, raw); e != nil { 375 | s.opts.logger.Errorf(ctx, "write error:%v", e.Error()) 376 | } 377 | return 378 | } 379 | } 380 | 381 | resp, _ := serialize.GetSerialize(s.opts.serialize).Marshal(err) 382 | raw, e := s.codec.Encode(codec.GeneralMsgType, codec.CompressTypeNot, uint64(time.Now().Unix()), []byte(""), []byte(""), []byte(""), resp) 383 | if e != nil { 384 | s.opts.logger.Errorf(ctx, "write error:%v", e.Error()) 385 | return 386 | } 387 | 388 | if e := util.Write(conn, raw); e != nil { 389 | s.opts.logger.Errorf(ctx, "write error:%v", e.Error()) 390 | } 391 | 392 | return 393 | } 394 | -------------------------------------------------------------------------------- /server/server_test.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "github.com/wsx864321/srpc/discov/etcd" 5 | "testing" 6 | ) 7 | 8 | func TestNewServer(t *testing.T) { 9 | s := NewServer(WithDiscovery(etcd.NewETCDRegister(etcd.WithEndpoints([]string{"127.0.0.1:2371"})))) 10 | s.Start() 11 | } 12 | -------------------------------------------------------------------------------- /trace/agent.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | 7 | "go.opentelemetry.io/otel" 8 | "go.opentelemetry.io/otel/exporters/jaeger" 9 | "go.opentelemetry.io/otel/propagation" 10 | "go.opentelemetry.io/otel/sdk/resource" 11 | tracesdk "go.opentelemetry.io/otel/sdk/trace" 12 | semconv "go.opentelemetry.io/otel/semconv/v1.4.0" 13 | ) 14 | 15 | var ( 16 | tp *tracesdk.TracerProvider 17 | once sync.Once 18 | defaultOptions = Options{ 19 | Enable: true, 20 | Url: "http://127.0.0.1:14268/api/traces", 21 | Sampler: 1.0, 22 | ServiceName: "srpc", 23 | } 24 | ) 25 | 26 | type Options struct { 27 | Enable bool 28 | Url string 29 | Sampler float64 30 | ServiceName string 31 | } 32 | 33 | type Option func(opt *Options) 34 | 35 | func newOptions(opts ...Option) *Options { 36 | o := defaultOptions 37 | for _, opt := range opts { 38 | opt(&o) 39 | } 40 | 41 | return &o 42 | } 43 | 44 | func WithEnable(enable bool) Option { 45 | return func(opt *Options) { 46 | opt.Enable = enable 47 | } 48 | } 49 | 50 | func WithUrl(url string) Option { 51 | return func(opt *Options) { 52 | opt.Url = url 53 | } 54 | } 55 | 56 | func WithSampler(sampler float64) Option { 57 | return func(opt *Options) { 58 | opt.Sampler = sampler 59 | } 60 | } 61 | 62 | func WithServiceName(name string) Option { 63 | return func(opt *Options) { 64 | opt.ServiceName = name 65 | } 66 | } 67 | 68 | // StartAgent 开启trace collector 69 | func StartAgent(opts ...Option) { 70 | o := newOptions(opts...) 71 | if !o.Enable { 72 | return 73 | } 74 | 75 | once.Do(func() { 76 | exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(o.Url))) 77 | if err != nil { 78 | return 79 | } 80 | 81 | tp = tracesdk.NewTracerProvider( 82 | tracesdk.WithSampler(tracesdk.TraceIDRatioBased(o.Sampler)), 83 | tracesdk.WithBatcher(exp), 84 | tracesdk.WithResource(resource.NewWithAttributes( 85 | semconv.SchemaURL, 86 | semconv.ServiceNameKey.String(o.ServiceName), 87 | )), 88 | ) 89 | 90 | otel.SetTracerProvider(tp) 91 | otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator( 92 | propagation.TraceContext{}, propagation.Baggage{})) 93 | }) 94 | } 95 | 96 | // StopAgent 关闭trace collector,在服务停止时调用StopAgent,不然可能造成trace数据的丢失 97 | func StopAgent() { 98 | _ = tp.Shutdown(context.TODO()) 99 | } 100 | -------------------------------------------------------------------------------- /trace/attr.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "go.opentelemetry.io/otel/attribute" 5 | semconv "go.opentelemetry.io/otel/semconv/v1.4.0" 6 | ) 7 | 8 | const ( 9 | // GRPCStatusCodeKey is convention for numeric status code of a gRPC request. 10 | GRPCStatusCodeKey = attribute.Key("rpc.grpc.status_code") 11 | // RPCNameKey is the name of message transmitted or received. 12 | RPCNameKey = attribute.Key("name") 13 | // RPCMessageTypeKey is the type of message transmitted or received. 14 | RPCMessageTypeKey = attribute.Key("message.type") 15 | // RPCMessageIDKey is the identifier of message transmitted or received. 16 | RPCMessageIDKey = attribute.Key("message.id") 17 | // RPCMessageCompressedSizeKey is the compressed size of the message transmitted or received in bytes. 18 | RPCMessageCompressedSizeKey = attribute.Key("message.compressed_size") 19 | // RPCMessageUncompressedSizeKey is the uncompressed size of the message 20 | // transmitted or received in bytes. 21 | RPCMessageUncompressedSizeKey = attribute.Key("message.uncompressed_size") 22 | // ServerEnvironment ... 23 | ServerEnvironment = attribute.Key("environment") 24 | ) 25 | 26 | // Semantic conventions for common RPC attributes. 27 | var ( 28 | // RPCSystemGRPC is the semantic convention for gRPC as the remoting system. 29 | RPCSystemGRPC = semconv.RPCSystemKey.String("grpc") 30 | // RPCNameMessage is the semantic convention for a message named message. 31 | RPCNameMessage = RPCNameKey.String("message") 32 | // RPCMessageTypeSent is the semantic conventions for sent RPC message types. 33 | RPCMessageTypeSent = RPCMessageTypeKey.String("SENT") 34 | // RPCMessageTypeReceived is the semantic conventions for the received RPC message types. 35 | RPCMessageTypeReceived = RPCMessageTypeKey.String("RECEIVED") 36 | ) 37 | 38 | // StatusCodeAttr returns an attribute.KeyValue that represents the give c. 39 | func StatusCodeAttr(c int32) attribute.KeyValue { 40 | return GRPCStatusCodeKey.Int64(int64(c)) 41 | } 42 | -------------------------------------------------------------------------------- /trace/trace.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "context" 5 | 6 | "go.opentelemetry.io/otel/propagation" 7 | sdktrace "go.opentelemetry.io/otel/trace" 8 | ) 9 | 10 | const ( 11 | TraceName = "srpc-trace" 12 | ) 13 | 14 | type metadataSupplier struct { 15 | metadata map[string]string 16 | } 17 | 18 | func (s *metadataSupplier) Get(key string) string { 19 | return s.metadata[key] 20 | } 21 | 22 | func (s *metadataSupplier) Set(key, value string) { 23 | s.metadata[key] = value 24 | } 25 | 26 | func (s *metadataSupplier) Keys() []string { 27 | out := make([]string, 0, len(s.metadata)) 28 | for key := range s.metadata { 29 | out = append(out, key) 30 | } 31 | 32 | return out 33 | } 34 | 35 | // Inject set cross-cutting concerns from the Context into the metadata. 36 | func Inject(ctx context.Context, p propagation.TextMapPropagator, m map[string]string) { 37 | p.Inject(ctx, &metadataSupplier{ 38 | metadata: m, 39 | }) 40 | } 41 | 42 | // Extract reads cross-cutting concerns from the metadata into a Context. 43 | func Extract(ctx context.Context, p propagation.TextMapPropagator, m map[string]string) sdktrace.SpanContext { 44 | ctx = p.Extract(ctx, &metadataSupplier{ 45 | metadata: m, 46 | }) 47 | 48 | return sdktrace.SpanContextFromContext(ctx) 49 | } 50 | -------------------------------------------------------------------------------- /trace/util.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "net" 5 | "strings" 6 | 7 | "go.opentelemetry.io/otel/attribute" 8 | semconv "go.opentelemetry.io/otel/semconv/v1.4.0" 9 | ) 10 | 11 | const ( 12 | localhost = "127.0.0.1" 13 | ) 14 | 15 | // BuildSpan returns the span info. 16 | func BuildSpan(method, peerAddr string) (string, []attribute.KeyValue) { 17 | attrs := make([]attribute.KeyValue, 0) 18 | name, mAttrs := parseServiceAndMethod(method) 19 | attrs = append(attrs, mAttrs...) 20 | attrs = append(attrs, peerAttr(peerAddr)...) 21 | return name, attrs 22 | } 23 | 24 | // parseServiceAndMethod ... 25 | func parseServiceAndMethod(fullMethod string) (string, []attribute.KeyValue) { 26 | name := strings.TrimLeft(fullMethod, "/") 27 | parts := strings.SplitN(name, "/", 2) 28 | if len(parts) != 2 { 29 | return name, []attribute.KeyValue(nil) 30 | } 31 | 32 | var attrs []attribute.KeyValue 33 | if service := parts[0]; service != "" { 34 | attrs = append(attrs, semconv.RPCServiceKey.String(service)) 35 | } 36 | if method := parts[1]; method != "" { 37 | attrs = append(attrs, semconv.RPCMethodKey.String(method)) 38 | } 39 | 40 | return name, attrs 41 | } 42 | 43 | // peerAttr returns the peer attributes. 44 | func peerAttr(addr string) []attribute.KeyValue { 45 | host, port, err := net.SplitHostPort(addr) 46 | if err != nil { 47 | return nil 48 | } 49 | 50 | if len(host) == 0 { 51 | host = localhost 52 | } 53 | 54 | return []attribute.KeyValue{ 55 | semconv.NetPeerIPKey.String(host), 56 | semconv.NetPeerPortKey.String(port), 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /transport/tcp.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import "net" 4 | 5 | type tcp struct { 6 | } 7 | 8 | func newTCP() ServerTransport { 9 | return &tcp{} 10 | } 11 | 12 | func (t *tcp) Listen(addr string) (net.Listener, error) { 13 | return net.Listen("tcp", addr) 14 | } 15 | -------------------------------------------------------------------------------- /transport/transport.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import "net" 4 | 5 | type Transport string 6 | 7 | const ( 8 | NetworkTCP Transport = "tcp" 9 | NetworkUDP = "udp" 10 | NetworkKCP = "kcp" 11 | NetworkHTTP = "http" 12 | NetworkQUIC = "quic" 13 | ) 14 | 15 | var transportMgr = map[Transport]ServerTransport{ 16 | NetworkTCP: newTCP(), 17 | NetworkUDP: newUDP(), 18 | } 19 | 20 | type ServerTransport interface { 21 | Listen(addr string) (net.Listener, error) 22 | } 23 | 24 | // GetTransport 获取传输协议 25 | func GetTransport(transport Transport) ServerTransport { 26 | return transportMgr[transport] 27 | } 28 | 29 | // RegisterTransport 注册传输协议 30 | func RegisterTransport(transport Transport, serverTransport ServerTransport) { 31 | transportMgr[transport] = serverTransport 32 | } 33 | -------------------------------------------------------------------------------- /transport/udp.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import "net" 4 | 5 | type udp struct { 6 | } 7 | 8 | func newUDP() ServerTransport { 9 | return &udp{} 10 | } 11 | 12 | func (u *udp) Listen(addr string) (net.Listener, error) { 13 | return net.Listen("udp", addr) 14 | } 15 | -------------------------------------------------------------------------------- /util/net.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import "net" 4 | 5 | // ReadFixData 读取网络连接对端发送的固定长度的内容 6 | func ReadFixData(conn net.Conn, buf []byte) error { 7 | var ( 8 | pos = 0 9 | totalSize = len(buf) 10 | ) 11 | for { 12 | c, err := conn.Read(buf[pos:]) 13 | if err != nil { 14 | return err 15 | } 16 | pos = pos + c 17 | if pos == totalSize { 18 | break 19 | } 20 | } 21 | 22 | return nil 23 | } 24 | 25 | // Write 对网络连接对端发送内容 26 | func Write(conn net.Conn, data []byte) error { 27 | totalLen := len(data) 28 | writeLen := 0 29 | for { 30 | len, err := conn.Write(data[writeLen:]) 31 | if err != nil { 32 | return err 33 | } 34 | writeLen = writeLen + len 35 | if writeLen >= totalLen { 36 | break 37 | } 38 | } 39 | return nil 40 | } 41 | -------------------------------------------------------------------------------- /util/slice.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | // InSliceString ... 4 | func InSliceString(s string, strs []string) bool { 5 | for _, val := range strs { 6 | if val == s { 7 | return true 8 | } 9 | } 10 | 11 | return false 12 | } 13 | --------------------------------------------------------------------------------