commit bd5a9fad97aeb3823a42c850b1e29d29ec457f4d Author: Jason Date: Thu Mar 17 15:55:27 2022 +0800 init diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6375407 --- /dev/null +++ b/Makefile @@ -0,0 +1,54 @@ +# Go parameters + +GOCMD=GO111MODULE=on go +GOBUILD=$(GOCMD) build +GOTEST=$(GOCMD) test + +PKG_LIST := `go list ./... | grep -v "vendor"` +PKG_LIST_VET := `go list ./... | grep -v "vendor"` + +all: test build +build: + rm -rf target/ + mkdir target/ + cp comet/conf/comet.toml target/comet.toml + cp logic/conf/logic.toml target/logic.toml + $(GOBUILD) -o target/comet comet/cmd/main.go + $(GOBUILD) -o target/logic logic/cmd/main.go + +test: + $(GOTEST) -v ./... + +clean: + rm -rf target/ + +run: + nohup target/logic -conf=target/logic.toml -logtostderr 2>&1 > target/logic.log & + nohup target/comet -conf=target/comet.toml -logtostderr 2>&1 > target/comet.log & + +vet: + @go vet ${PKG_LIST_VET} + +stop: + pkill -f target/comet + pkill -f target/logic + +# 编译oa二进制 amd64 +quick_build_amd: + @echo '┌ start quick build amd64' + @bash ./script/build/quick_build.sh + @echo '└ end quick build amd64' + +# 编译oa二进制 amr64 +quick_build_arm: + @echo '┌ start quick build arm64' + @bash ./script/build/quick_build.sh arm64 + @echo '└ end quick build arm64' + +build_linux_amd: + rm -rf target/ + mkdir target/ + cp comet/conf/comet.toml target/comet.toml + cp logic/conf/logic.toml target/logic.toml + GOOS=linux GOARCH=amd64 GO111MODULE=on GOPROXY=https://goproxy.cn,direct GOSUMDB="sum.golang.google.cn" go build -v -o target/comet comet/cmd/main.go + GOOS=linux GOARCH=amd64 GO111MODULE=on GOPROXY=https://goproxy.cn,direct GOSUMDB="sum.golang.google.cn" go build -v -o target/logic logic/cmd/main.go diff --git a/README.md b/README.md new file mode 100644 index 0000000..28ef6b0 --- /dev/null +++ b/README.md @@ -0,0 +1,66 @@ +goim v2.0 +============== + +goim is a im server writen by golang. + +## Features + * Light weight + * High performance + * Pure Golang + * Supports single push, multiple push and broadcasting + * Supports one key to multiple subscribers (Configurable maximum subscribers count) + * Supports heartbeats (Application heartbeats, TCP, KeepAlive, HTTP long pulling) + * Supports authentication (Unauthenticated user can't subscribe) + * Supports multiple protocols (WebSocket,TCP,HTTP) + * Scalable architecture (Unlimited dynamic `comet` and `logic` modules) + * Asynchronous push notification based on Kafka + +## Architecture +![arch](./docs/goim-arch.png) + +## Quick Start + +### Dependencies + * kakfa: 消息队列 + * redis: 保存客户端连接信息 + * etcd: 提供服务注册/发现, comet、logic 服务注册在 etcd + +docker 快速启动相关依赖: +``` + docker-compose -f benchmarks/kafka-docker-compose.yml up -d + docker run -d -p 6379:6379 redis + docker run -d -p 2379:2379 -p 2380:2380 --name etcd-v3.4.13 quay.io/coreos/etcd:v3.4.13 /usr/local/bin/etcd \ + --name s1 \ + --data-dir /etcd-data \ + --listen-client-urls http://0.0.0.0:2379 \ + --advertise-client-urls http://0.0.0.0:2379 \ + --listen-peer-urls http://0.0.0.0:2380 \ + --initial-advertise-peer-urls http://0.0.0.0:2380 \ + --initial-cluster s1=http://0.0.0.0:2380 \ + --initial-cluster-token tkn \ + --initial-cluster-state new \ + --log-level info \ + --logger zap \ + --log-outputs stderr +``` + +### Build +``` + make build +``` + +### Run +``` + make run + make stop + + // or + nohup target/logic -conf=target/logic.toml -logtostderr 2>&1 > target/logic.log & + nohup target/comet -conf=target/comet.toml -logtostderr 2>&1 > target/comet.log & +``` + +### Configuration +You can view the comments in `target/comet.toml`, `target/logic.toml` to understand the meaning of the config. + +## Document +[Protocol](./docs/proto.md) \ No newline at end of file diff --git a/api/comet/grpc/comet.pb.go b/api/comet/grpc/comet.pb.go new file mode 100644 index 0000000..308c421 --- /dev/null +++ b/api/comet/grpc/comet.pb.go @@ -0,0 +1,1294 @@ +// protoc -I=. -I=$GOPATH/src --go_out=plugins=grpc:. *.proto + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.19.1 +// source: comet.proto + +package grpc + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Op int32 + +const ( + Op_Undefined Op = 0 + Op_Auth Op = 1 + Op_AuthReply Op = 2 + Op_Heartbeat Op = 3 + Op_HeartbeatReply Op = 4 + Op_Disconnect Op = 5 + Op_DisconnectReply Op = 6 + Op_SendMsg Op = 7 + Op_SendMsgReply Op = 8 //客户端回复消息已收到 + Op_ReceiveMsg Op = 9 + Op_ReceiveMsgReply Op = 10 + Op_ProtoReady Op = 11 + Op_ProtoFinish Op = 12 + Op_Raw Op = 13 + Op_SyncMsgReq Op = 14 + Op_SyncMsgReply Op = 15 + Op_RePush Op = 16 +) + +// Enum value maps for Op. +var ( + Op_name = map[int32]string{ + 0: "Undefined", + 1: "Auth", + 2: "AuthReply", + 3: "Heartbeat", + 4: "HeartbeatReply", + 5: "Disconnect", + 6: "DisconnectReply", + 7: "SendMsg", + 8: "SendMsgReply", + 9: "ReceiveMsg", + 10: "ReceiveMsgReply", + 11: "ProtoReady", + 12: "ProtoFinish", + 13: "Raw", + 14: "SyncMsgReq", + 15: "SyncMsgReply", + 16: "RePush", + } + Op_value = map[string]int32{ + "Undefined": 0, + "Auth": 1, + "AuthReply": 2, + "Heartbeat": 3, + "HeartbeatReply": 4, + "Disconnect": 5, + "DisconnectReply": 6, + "SendMsg": 7, + "SendMsgReply": 8, + "ReceiveMsg": 9, + "ReceiveMsgReply": 10, + "ProtoReady": 11, + "ProtoFinish": 12, + "Raw": 13, + "SyncMsgReq": 14, + "SyncMsgReply": 15, + "RePush": 16, + } +) + +func (x Op) Enum() *Op { + p := new(Op) + *p = x + return p +} + +func (x Op) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Op) Descriptor() protoreflect.EnumDescriptor { + return file_comet_proto_enumTypes[0].Descriptor() +} + +func (Op) Type() protoreflect.EnumType { + return &file_comet_proto_enumTypes[0] +} + +func (x Op) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Op.Descriptor instead. +func (Op) EnumDescriptor() ([]byte, []int) { + return file_comet_proto_rawDescGZIP(), []int{0} +} + +type Proto struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Ver int32 `protobuf:"varint,1,opt,name=ver,proto3" json:"ver,omitempty"` + Op int32 `protobuf:"varint,2,opt,name=op,proto3" json:"op,omitempty"` + Seq int32 `protobuf:"varint,3,opt,name=seq,proto3" json:"seq,omitempty"` + Ack int32 `protobuf:"varint,4,opt,name=ack,proto3" json:"ack,omitempty"` + Body []byte `protobuf:"bytes,5,opt,name=body,proto3" json:"body,omitempty"` +} + +func (x *Proto) Reset() { + *x = Proto{} + if protoimpl.UnsafeEnabled { + mi := &file_comet_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Proto) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Proto) ProtoMessage() {} + +func (x *Proto) ProtoReflect() protoreflect.Message { + mi := &file_comet_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Proto.ProtoReflect.Descriptor instead. +func (*Proto) Descriptor() ([]byte, []int) { + return file_comet_proto_rawDescGZIP(), []int{0} +} + +func (x *Proto) GetVer() int32 { + if x != nil { + return x.Ver + } + return 0 +} + +func (x *Proto) GetOp() int32 { + if x != nil { + return x.Op + } + return 0 +} + +func (x *Proto) GetSeq() int32 { + if x != nil { + return x.Seq + } + return 0 +} + +func (x *Proto) GetAck() int32 { + if x != nil { + return x.Ack + } + return 0 +} + +func (x *Proto) GetBody() []byte { + if x != nil { + return x.Body + } + return nil +} + +// Proto 中 Op 为 OpAuth 时, body 必须可以反序列化为 AuthMsg +type AuthMsg struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AppId string `protobuf:"bytes,1,opt,name=appId,proto3" json:"appId,omitempty"` + Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"` + Ext []byte `protobuf:"bytes,3,opt,name=ext,proto3" json:"ext,omitempty"` // 其它业务方可能需要的信息 +} + +func (x *AuthMsg) Reset() { + *x = AuthMsg{} + if protoimpl.UnsafeEnabled { + mi := &file_comet_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AuthMsg) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AuthMsg) ProtoMessage() {} + +func (x *AuthMsg) ProtoReflect() protoreflect.Message { + mi := &file_comet_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AuthMsg.ProtoReflect.Descriptor instead. +func (*AuthMsg) Descriptor() ([]byte, []int) { + return file_comet_proto_rawDescGZIP(), []int{1} +} + +func (x *AuthMsg) GetAppId() string { + if x != nil { + return x.AppId + } + return "" +} + +func (x *AuthMsg) GetToken() string { + if x != nil { + return x.Token + } + return "" +} + +func (x *AuthMsg) GetExt() []byte { + if x != nil { + return x.Ext + } + return nil +} + +// Proto 中 Op 为 SyncMsgReply 时, body 必须可以反序列化为 SyncMsg +type SyncMsg struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + LogId int64 `protobuf:"varint,1,opt,name=logId,proto3" json:"logId,omitempty"` +} + +func (x *SyncMsg) Reset() { + *x = SyncMsg{} + if protoimpl.UnsafeEnabled { + mi := &file_comet_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SyncMsg) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncMsg) ProtoMessage() {} + +func (x *SyncMsg) ProtoReflect() protoreflect.Message { + mi := &file_comet_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SyncMsg.ProtoReflect.Descriptor instead. +func (*SyncMsg) Descriptor() ([]byte, []int) { + return file_comet_proto_rawDescGZIP(), []int{2} +} + +func (x *SyncMsg) GetLogId() int64 { + if x != nil { + return x.LogId + } + return 0 +} + +type Empty struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Empty) Reset() { + *x = Empty{} + if protoimpl.UnsafeEnabled { + mi := &file_comet_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Empty) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Empty) ProtoMessage() {} + +func (x *Empty) ProtoReflect() protoreflect.Message { + mi := &file_comet_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Empty.ProtoReflect.Descriptor instead. +func (*Empty) Descriptor() ([]byte, []int) { + return file_comet_proto_rawDescGZIP(), []int{3} +} + +type PushMsgReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Keys []string `protobuf:"bytes,1,rep,name=keys,proto3" json:"keys,omitempty"` + ProtoOp int32 `protobuf:"varint,2,opt,name=protoOp,proto3" json:"protoOp,omitempty"` + Proto *Proto `protobuf:"bytes,3,opt,name=proto,proto3" json:"proto,omitempty"` +} + +func (x *PushMsgReq) Reset() { + *x = PushMsgReq{} + if protoimpl.UnsafeEnabled { + mi := &file_comet_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PushMsgReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PushMsgReq) ProtoMessage() {} + +func (x *PushMsgReq) ProtoReflect() protoreflect.Message { + mi := &file_comet_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PushMsgReq.ProtoReflect.Descriptor instead. +func (*PushMsgReq) Descriptor() ([]byte, []int) { + return file_comet_proto_rawDescGZIP(), []int{4} +} + +func (x *PushMsgReq) GetKeys() []string { + if x != nil { + return x.Keys + } + return nil +} + +func (x *PushMsgReq) GetProtoOp() int32 { + if x != nil { + return x.ProtoOp + } + return 0 +} + +func (x *PushMsgReq) GetProto() *Proto { + if x != nil { + return x.Proto + } + return nil +} + +type PushMsgReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Index map[string]int32 `protobuf:"bytes,1,rep,name=index,proto3" json:"index,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` +} + +func (x *PushMsgReply) Reset() { + *x = PushMsgReply{} + if protoimpl.UnsafeEnabled { + mi := &file_comet_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PushMsgReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PushMsgReply) ProtoMessage() {} + +func (x *PushMsgReply) ProtoReflect() protoreflect.Message { + mi := &file_comet_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PushMsgReply.ProtoReflect.Descriptor instead. +func (*PushMsgReply) Descriptor() ([]byte, []int) { + return file_comet_proto_rawDescGZIP(), []int{5} +} + +func (x *PushMsgReply) GetIndex() map[string]int32 { + if x != nil { + return x.Index + } + return nil +} + +type BroadcastReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProtoOp int32 `protobuf:"varint,1,opt,name=protoOp,proto3" json:"protoOp,omitempty"` + Proto *Proto `protobuf:"bytes,2,opt,name=proto,proto3" json:"proto,omitempty"` +} + +func (x *BroadcastReq) Reset() { + *x = BroadcastReq{} + if protoimpl.UnsafeEnabled { + mi := &file_comet_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BroadcastReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BroadcastReq) ProtoMessage() {} + +func (x *BroadcastReq) ProtoReflect() protoreflect.Message { + mi := &file_comet_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BroadcastReq.ProtoReflect.Descriptor instead. +func (*BroadcastReq) Descriptor() ([]byte, []int) { + return file_comet_proto_rawDescGZIP(), []int{6} +} + +func (x *BroadcastReq) GetProtoOp() int32 { + if x != nil { + return x.ProtoOp + } + return 0 +} + +func (x *BroadcastReq) GetProto() *Proto { + if x != nil { + return x.Proto + } + return nil +} + +type BroadcastReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *BroadcastReply) Reset() { + *x = BroadcastReply{} + if protoimpl.UnsafeEnabled { + mi := &file_comet_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BroadcastReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BroadcastReply) ProtoMessage() {} + +func (x *BroadcastReply) ProtoReflect() protoreflect.Message { + mi := &file_comet_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BroadcastReply.ProtoReflect.Descriptor instead. +func (*BroadcastReply) Descriptor() ([]byte, []int) { + return file_comet_proto_rawDescGZIP(), []int{7} +} + +type BroadcastGroupReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + GroupID string `protobuf:"bytes,1,opt,name=groupID,proto3" json:"groupID,omitempty"` + Proto *Proto `protobuf:"bytes,2,opt,name=proto,proto3" json:"proto,omitempty"` +} + +func (x *BroadcastGroupReq) Reset() { + *x = BroadcastGroupReq{} + if protoimpl.UnsafeEnabled { + mi := &file_comet_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BroadcastGroupReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BroadcastGroupReq) ProtoMessage() {} + +func (x *BroadcastGroupReq) ProtoReflect() protoreflect.Message { + mi := &file_comet_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BroadcastGroupReq.ProtoReflect.Descriptor instead. +func (*BroadcastGroupReq) Descriptor() ([]byte, []int) { + return file_comet_proto_rawDescGZIP(), []int{8} +} + +func (x *BroadcastGroupReq) GetGroupID() string { + if x != nil { + return x.GroupID + } + return "" +} + +func (x *BroadcastGroupReq) GetProto() *Proto { + if x != nil { + return x.Proto + } + return nil +} + +type BroadcastGroupReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *BroadcastGroupReply) Reset() { + *x = BroadcastGroupReply{} + if protoimpl.UnsafeEnabled { + mi := &file_comet_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BroadcastGroupReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BroadcastGroupReply) ProtoMessage() {} + +func (x *BroadcastGroupReply) ProtoReflect() protoreflect.Message { + mi := &file_comet_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BroadcastGroupReply.ProtoReflect.Descriptor instead. +func (*BroadcastGroupReply) Descriptor() ([]byte, []int) { + return file_comet_proto_rawDescGZIP(), []int{9} +} + +type JoinGroupsReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Keys []string `protobuf:"bytes,1,rep,name=keys,proto3" json:"keys,omitempty"` + Gid []string `protobuf:"bytes,2,rep,name=gid,proto3" json:"gid,omitempty"` +} + +func (x *JoinGroupsReq) Reset() { + *x = JoinGroupsReq{} + if protoimpl.UnsafeEnabled { + mi := &file_comet_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *JoinGroupsReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JoinGroupsReq) ProtoMessage() {} + +func (x *JoinGroupsReq) ProtoReflect() protoreflect.Message { + mi := &file_comet_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JoinGroupsReq.ProtoReflect.Descriptor instead. +func (*JoinGroupsReq) Descriptor() ([]byte, []int) { + return file_comet_proto_rawDescGZIP(), []int{10} +} + +func (x *JoinGroupsReq) GetKeys() []string { + if x != nil { + return x.Keys + } + return nil +} + +func (x *JoinGroupsReq) GetGid() []string { + if x != nil { + return x.Gid + } + return nil +} + +type JoinGroupsReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *JoinGroupsReply) Reset() { + *x = JoinGroupsReply{} + if protoimpl.UnsafeEnabled { + mi := &file_comet_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *JoinGroupsReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*JoinGroupsReply) ProtoMessage() {} + +func (x *JoinGroupsReply) ProtoReflect() protoreflect.Message { + mi := &file_comet_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use JoinGroupsReply.ProtoReflect.Descriptor instead. +func (*JoinGroupsReply) Descriptor() ([]byte, []int) { + return file_comet_proto_rawDescGZIP(), []int{11} +} + +type LeaveGroupsReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Keys []string `protobuf:"bytes,1,rep,name=keys,proto3" json:"keys,omitempty"` + Gid []string `protobuf:"bytes,2,rep,name=gid,proto3" json:"gid,omitempty"` +} + +func (x *LeaveGroupsReq) Reset() { + *x = LeaveGroupsReq{} + if protoimpl.UnsafeEnabled { + mi := &file_comet_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LeaveGroupsReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LeaveGroupsReq) ProtoMessage() {} + +func (x *LeaveGroupsReq) ProtoReflect() protoreflect.Message { + mi := &file_comet_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LeaveGroupsReq.ProtoReflect.Descriptor instead. +func (*LeaveGroupsReq) Descriptor() ([]byte, []int) { + return file_comet_proto_rawDescGZIP(), []int{12} +} + +func (x *LeaveGroupsReq) GetKeys() []string { + if x != nil { + return x.Keys + } + return nil +} + +func (x *LeaveGroupsReq) GetGid() []string { + if x != nil { + return x.Gid + } + return nil +} + +type LeaveGroupsReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *LeaveGroupsReply) Reset() { + *x = LeaveGroupsReply{} + if protoimpl.UnsafeEnabled { + mi := &file_comet_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LeaveGroupsReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LeaveGroupsReply) ProtoMessage() {} + +func (x *LeaveGroupsReply) ProtoReflect() protoreflect.Message { + mi := &file_comet_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LeaveGroupsReply.ProtoReflect.Descriptor instead. +func (*LeaveGroupsReply) Descriptor() ([]byte, []int) { + return file_comet_proto_rawDescGZIP(), []int{13} +} + +type DelGroupsReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Gid []string `protobuf:"bytes,1,rep,name=gid,proto3" json:"gid,omitempty"` +} + +func (x *DelGroupsReq) Reset() { + *x = DelGroupsReq{} + if protoimpl.UnsafeEnabled { + mi := &file_comet_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DelGroupsReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DelGroupsReq) ProtoMessage() {} + +func (x *DelGroupsReq) ProtoReflect() protoreflect.Message { + mi := &file_comet_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DelGroupsReq.ProtoReflect.Descriptor instead. +func (*DelGroupsReq) Descriptor() ([]byte, []int) { + return file_comet_proto_rawDescGZIP(), []int{14} +} + +func (x *DelGroupsReq) GetGid() []string { + if x != nil { + return x.Gid + } + return nil +} + +type DelGroupsReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *DelGroupsReply) Reset() { + *x = DelGroupsReply{} + if protoimpl.UnsafeEnabled { + mi := &file_comet_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DelGroupsReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DelGroupsReply) ProtoMessage() {} + +func (x *DelGroupsReply) ProtoReflect() protoreflect.Message { + mi := &file_comet_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DelGroupsReply.ProtoReflect.Descriptor instead. +func (*DelGroupsReply) Descriptor() ([]byte, []int) { + return file_comet_proto_rawDescGZIP(), []int{15} +} + +var File_comet_proto protoreflect.FileDescriptor + +var file_comet_proto_rawDesc = []byte{ + 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x69, + 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x22, 0x61, 0x0a, 0x05, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x10, 0x0a, 0x03, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x76, + 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, + 0x6f, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x65, 0x71, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x03, 0x73, 0x65, 0x71, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x63, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x03, 0x61, 0x63, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x47, 0x0a, 0x07, 0x41, 0x75, + 0x74, 0x68, 0x4d, 0x73, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x49, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x70, 0x70, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, + 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, + 0x65, 0x78, 0x74, 0x22, 0x1f, 0x0a, 0x07, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x73, 0x67, 0x12, 0x14, + 0x0a, 0x05, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, + 0x6f, 0x67, 0x49, 0x64, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x61, 0x0a, + 0x0a, 0x50, 0x75, 0x73, 0x68, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x6b, + 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, + 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x4f, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x07, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x4f, 0x70, 0x12, 0x25, 0x0a, 0x05, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x69, 0x6d, 0x2e, 0x63, 0x6f, + 0x6d, 0x65, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x22, 0x81, 0x01, 0x0a, 0x0c, 0x50, 0x75, 0x73, 0x68, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x12, 0x37, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x21, 0x2e, 0x69, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x2e, 0x50, 0x75, 0x73, 0x68, + 0x4d, 0x73, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x2e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x1a, 0x38, 0x0a, 0x0a, 0x49, 0x6e, + 0x64, 0x65, 0x78, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4f, 0x0a, 0x0c, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, + 0x74, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x4f, 0x70, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x4f, 0x70, 0x12, 0x25, + 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, + 0x69, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x05, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x10, 0x0a, 0x0e, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, + 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x54, 0x0a, 0x11, 0x42, 0x72, 0x6f, 0x61, 0x64, + 0x63, 0x61, 0x73, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a, 0x07, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x12, 0x25, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x69, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, + 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x15, 0x0a, + 0x13, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x22, 0x35, 0x0a, 0x0d, 0x4a, 0x6f, 0x69, 0x6e, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x73, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x67, 0x69, 0x64, 0x22, 0x11, 0x0a, 0x0f, 0x4a, + 0x6f, 0x69, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x36, + 0x0a, 0x0e, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, + 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, + 0x6b, 0x65, 0x79, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x69, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x03, 0x67, 0x69, 0x64, 0x22, 0x12, 0x0a, 0x10, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x20, 0x0a, 0x0c, 0x44, 0x65, + 0x6c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x67, 0x69, 0x64, 0x22, 0x10, 0x0a, 0x0e, + 0x44, 0x65, 0x6c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x2a, 0x90, + 0x02, 0x0a, 0x02, 0x4f, 0x70, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x65, 0x64, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x75, 0x74, 0x68, 0x10, 0x01, 0x12, 0x0d, + 0x0a, 0x09, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x10, 0x02, 0x12, 0x0d, 0x0a, + 0x09, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, + 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x10, 0x04, + 0x12, 0x0e, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x10, 0x05, + 0x12, 0x13, 0x0a, 0x0f, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, + 0x70, 0x6c, 0x79, 0x10, 0x06, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, + 0x10, 0x07, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x10, 0x08, 0x12, 0x0e, 0x0a, 0x0a, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x4d, + 0x73, 0x67, 0x10, 0x09, 0x12, 0x13, 0x0a, 0x0f, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x4d, + 0x73, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x10, 0x0a, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x52, 0x65, 0x61, 0x64, 0x79, 0x10, 0x0b, 0x12, 0x0f, 0x0a, 0x0b, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x10, 0x0c, 0x12, 0x07, 0x0a, 0x03, 0x52, 0x61, + 0x77, 0x10, 0x0d, 0x12, 0x0e, 0x0a, 0x0a, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x73, 0x67, 0x52, 0x65, + 0x71, 0x10, 0x0e, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x73, 0x67, 0x52, 0x65, + 0x70, 0x6c, 0x79, 0x10, 0x0f, 0x12, 0x0a, 0x0a, 0x06, 0x52, 0x65, 0x50, 0x75, 0x73, 0x68, 0x10, + 0x10, 0x32, 0x93, 0x03, 0x0a, 0x05, 0x43, 0x6f, 0x6d, 0x65, 0x74, 0x12, 0x37, 0x0a, 0x07, 0x50, + 0x75, 0x73, 0x68, 0x4d, 0x73, 0x67, 0x12, 0x14, 0x2e, 0x69, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x65, + 0x74, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x69, + 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x4d, 0x73, 0x67, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x12, 0x3d, 0x0a, 0x09, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, + 0x74, 0x12, 0x16, 0x2e, 0x69, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x2e, 0x42, 0x72, 0x6f, + 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x69, 0x6d, 0x2e, 0x63, + 0x6f, 0x6d, 0x65, 0x74, 0x2e, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, + 0x70, 0x6c, 0x79, 0x12, 0x4c, 0x0a, 0x0e, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x1b, 0x2e, 0x69, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, + 0x2e, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, + 0x65, 0x71, 0x1a, 0x1d, 0x2e, 0x69, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x2e, 0x42, 0x72, + 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x12, 0x40, 0x0a, 0x0a, 0x4a, 0x6f, 0x69, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, + 0x17, 0x2e, 0x69, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x19, 0x2e, 0x69, 0x6d, 0x2e, 0x63, 0x6f, + 0x6d, 0x65, 0x74, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, + 0x70, 0x6c, 0x79, 0x12, 0x43, 0x0a, 0x0b, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x73, 0x12, 0x18, 0x2e, 0x69, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x2e, 0x4c, 0x65, + 0x61, 0x76, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1a, 0x2e, 0x69, + 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x2e, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x3d, 0x0a, 0x09, 0x44, 0x65, 0x6c, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x16, 0x2e, 0x69, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, + 0x2e, 0x44, 0x65, 0x6c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, + 0x69, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x2e, 0x44, 0x65, 0x6c, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x42, 0x25, 0x5a, 0x23, 0x67, 0x69, 0x74, 0x6c, 0x61, + 0x62, 0x2e, 0x33, 0x33, 0x2e, 0x63, 0x6e, 0x2f, 0x63, 0x68, 0x61, 0x74, 0x2f, 0x69, 0x6d, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_comet_proto_rawDescOnce sync.Once + file_comet_proto_rawDescData = file_comet_proto_rawDesc +) + +func file_comet_proto_rawDescGZIP() []byte { + file_comet_proto_rawDescOnce.Do(func() { + file_comet_proto_rawDescData = protoimpl.X.CompressGZIP(file_comet_proto_rawDescData) + }) + return file_comet_proto_rawDescData +} + +var file_comet_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_comet_proto_msgTypes = make([]protoimpl.MessageInfo, 17) +var file_comet_proto_goTypes = []interface{}{ + (Op)(0), // 0: im.comet.Op + (*Proto)(nil), // 1: im.comet.Proto + (*AuthMsg)(nil), // 2: im.comet.AuthMsg + (*SyncMsg)(nil), // 3: im.comet.SyncMsg + (*Empty)(nil), // 4: im.comet.Empty + (*PushMsgReq)(nil), // 5: im.comet.PushMsgReq + (*PushMsgReply)(nil), // 6: im.comet.PushMsgReply + (*BroadcastReq)(nil), // 7: im.comet.BroadcastReq + (*BroadcastReply)(nil), // 8: im.comet.BroadcastReply + (*BroadcastGroupReq)(nil), // 9: im.comet.BroadcastGroupReq + (*BroadcastGroupReply)(nil), // 10: im.comet.BroadcastGroupReply + (*JoinGroupsReq)(nil), // 11: im.comet.JoinGroupsReq + (*JoinGroupsReply)(nil), // 12: im.comet.JoinGroupsReply + (*LeaveGroupsReq)(nil), // 13: im.comet.LeaveGroupsReq + (*LeaveGroupsReply)(nil), // 14: im.comet.LeaveGroupsReply + (*DelGroupsReq)(nil), // 15: im.comet.DelGroupsReq + (*DelGroupsReply)(nil), // 16: im.comet.DelGroupsReply + nil, // 17: im.comet.PushMsgReply.IndexEntry +} +var file_comet_proto_depIdxs = []int32{ + 1, // 0: im.comet.PushMsgReq.proto:type_name -> im.comet.Proto + 17, // 1: im.comet.PushMsgReply.index:type_name -> im.comet.PushMsgReply.IndexEntry + 1, // 2: im.comet.BroadcastReq.proto:type_name -> im.comet.Proto + 1, // 3: im.comet.BroadcastGroupReq.proto:type_name -> im.comet.Proto + 5, // 4: im.comet.Comet.PushMsg:input_type -> im.comet.PushMsgReq + 7, // 5: im.comet.Comet.Broadcast:input_type -> im.comet.BroadcastReq + 9, // 6: im.comet.Comet.BroadcastGroup:input_type -> im.comet.BroadcastGroupReq + 11, // 7: im.comet.Comet.JoinGroups:input_type -> im.comet.JoinGroupsReq + 13, // 8: im.comet.Comet.LeaveGroups:input_type -> im.comet.LeaveGroupsReq + 15, // 9: im.comet.Comet.DelGroups:input_type -> im.comet.DelGroupsReq + 6, // 10: im.comet.Comet.PushMsg:output_type -> im.comet.PushMsgReply + 8, // 11: im.comet.Comet.Broadcast:output_type -> im.comet.BroadcastReply + 10, // 12: im.comet.Comet.BroadcastGroup:output_type -> im.comet.BroadcastGroupReply + 12, // 13: im.comet.Comet.JoinGroups:output_type -> im.comet.JoinGroupsReply + 14, // 14: im.comet.Comet.LeaveGroups:output_type -> im.comet.LeaveGroupsReply + 16, // 15: im.comet.Comet.DelGroups:output_type -> im.comet.DelGroupsReply + 10, // [10:16] is the sub-list for method output_type + 4, // [4:10] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name +} + +func init() { file_comet_proto_init() } +func file_comet_proto_init() { + if File_comet_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_comet_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Proto); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_comet_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AuthMsg); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_comet_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SyncMsg); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_comet_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Empty); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_comet_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PushMsgReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_comet_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PushMsgReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_comet_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BroadcastReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_comet_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BroadcastReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_comet_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BroadcastGroupReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_comet_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BroadcastGroupReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_comet_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*JoinGroupsReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_comet_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*JoinGroupsReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_comet_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LeaveGroupsReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_comet_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LeaveGroupsReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_comet_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DelGroupsReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_comet_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DelGroupsReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_comet_proto_rawDesc, + NumEnums: 1, + NumMessages: 17, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_comet_proto_goTypes, + DependencyIndexes: file_comet_proto_depIdxs, + EnumInfos: file_comet_proto_enumTypes, + MessageInfos: file_comet_proto_msgTypes, + }.Build() + File_comet_proto = out.File + file_comet_proto_rawDesc = nil + file_comet_proto_goTypes = nil + file_comet_proto_depIdxs = nil +} diff --git a/api/comet/grpc/comet.proto b/api/comet/grpc/comet.proto new file mode 100644 index 0000000..871fc60 --- /dev/null +++ b/api/comet/grpc/comet.proto @@ -0,0 +1,109 @@ +// protoc -I=. -I=$GOPATH/src --go_out=plugins=grpc:. *.proto +syntax = "proto3"; + +package im.comet; +option go_package = "gitlab.33.cn/chat/im/api/comet/grpc"; + +message Proto { + int32 ver = 1; + int32 op = 2; + int32 seq = 3; + int32 ack = 4; + bytes body = 5; +} + +enum Op { + Undefined = 0; + + Auth = 1; + AuthReply = 2; + + Heartbeat = 3; + HeartbeatReply = 4; + + Disconnect = 5; + DisconnectReply = 6; + + SendMsg = 7; + SendMsgReply = 8; //客户端回复消息已收到 + + ReceiveMsg = 9; + ReceiveMsgReply = 10; + + ProtoReady = 11; + ProtoFinish = 12; + + Raw = 13; + + SyncMsgReq = 14; + SyncMsgReply = 15; + + RePush = 16; +} + +// Proto 中 Op 为 OpAuth 时, body 必须可以反序列化为 AuthMsg +message AuthMsg { + string appId = 1; + string token = 2; + bytes ext = 3; // 其它业务方可能需要的信息 +} + +// Proto 中 Op 为 SyncMsgReply 时, body 必须可以反序列化为 SyncMsg +message SyncMsg { + int64 logId = 1; +} + +message Empty{} + +message PushMsgReq { + repeated string keys = 1; + int32 protoOp = 2; + Proto proto = 3; +} + +message PushMsgReply { + map index = 1; +} + +message BroadcastReq{ + int32 protoOp = 1; + Proto proto = 2; +} + +message BroadcastReply{} + +message BroadcastGroupReq { + string groupID = 1; + Proto proto = 2; +} + +message BroadcastGroupReply{} + +message JoinGroupsReq { + repeated string keys = 1; + repeated string gid = 2; +} + +message JoinGroupsReply{} + +message LeaveGroupsReq { + repeated string keys = 1; + repeated string gid = 2; +} + +message LeaveGroupsReply{} + +message DelGroupsReq { + repeated string gid = 1; +} + +message DelGroupsReply{} + +service Comet { + rpc PushMsg(PushMsgReq) returns (PushMsgReply); + rpc Broadcast(BroadcastReq) returns (BroadcastReply); + rpc BroadcastGroup(BroadcastGroupReq) returns (BroadcastGroupReply); + rpc JoinGroups(JoinGroupsReq) returns (JoinGroupsReply); + rpc LeaveGroups(LeaveGroupsReq) returns (LeaveGroupsReply); + rpc DelGroups(DelGroupsReq) returns (DelGroupsReply); +} diff --git a/api/comet/grpc/comet_grpc.pb.go b/api/comet/grpc/comet_grpc.pb.go new file mode 100644 index 0000000..993f7c2 --- /dev/null +++ b/api/comet/grpc/comet_grpc.pb.go @@ -0,0 +1,281 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package grpc + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// CometClient is the client API for Comet service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type CometClient interface { + PushMsg(ctx context.Context, in *PushMsgReq, opts ...grpc.CallOption) (*PushMsgReply, error) + Broadcast(ctx context.Context, in *BroadcastReq, opts ...grpc.CallOption) (*BroadcastReply, error) + BroadcastGroup(ctx context.Context, in *BroadcastGroupReq, opts ...grpc.CallOption) (*BroadcastGroupReply, error) + JoinGroups(ctx context.Context, in *JoinGroupsReq, opts ...grpc.CallOption) (*JoinGroupsReply, error) + LeaveGroups(ctx context.Context, in *LeaveGroupsReq, opts ...grpc.CallOption) (*LeaveGroupsReply, error) + DelGroups(ctx context.Context, in *DelGroupsReq, opts ...grpc.CallOption) (*DelGroupsReply, error) +} + +type cometClient struct { + cc grpc.ClientConnInterface +} + +func NewCometClient(cc grpc.ClientConnInterface) CometClient { + return &cometClient{cc} +} + +func (c *cometClient) PushMsg(ctx context.Context, in *PushMsgReq, opts ...grpc.CallOption) (*PushMsgReply, error) { + out := new(PushMsgReply) + err := c.cc.Invoke(ctx, "/im.comet.Comet/PushMsg", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cometClient) Broadcast(ctx context.Context, in *BroadcastReq, opts ...grpc.CallOption) (*BroadcastReply, error) { + out := new(BroadcastReply) + err := c.cc.Invoke(ctx, "/im.comet.Comet/Broadcast", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cometClient) BroadcastGroup(ctx context.Context, in *BroadcastGroupReq, opts ...grpc.CallOption) (*BroadcastGroupReply, error) { + out := new(BroadcastGroupReply) + err := c.cc.Invoke(ctx, "/im.comet.Comet/BroadcastGroup", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cometClient) JoinGroups(ctx context.Context, in *JoinGroupsReq, opts ...grpc.CallOption) (*JoinGroupsReply, error) { + out := new(JoinGroupsReply) + err := c.cc.Invoke(ctx, "/im.comet.Comet/JoinGroups", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cometClient) LeaveGroups(ctx context.Context, in *LeaveGroupsReq, opts ...grpc.CallOption) (*LeaveGroupsReply, error) { + out := new(LeaveGroupsReply) + err := c.cc.Invoke(ctx, "/im.comet.Comet/LeaveGroups", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cometClient) DelGroups(ctx context.Context, in *DelGroupsReq, opts ...grpc.CallOption) (*DelGroupsReply, error) { + out := new(DelGroupsReply) + err := c.cc.Invoke(ctx, "/im.comet.Comet/DelGroups", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// CometServer is the server API for Comet service. +// All implementations must embed UnimplementedCometServer +// for forward compatibility +type CometServer interface { + PushMsg(context.Context, *PushMsgReq) (*PushMsgReply, error) + Broadcast(context.Context, *BroadcastReq) (*BroadcastReply, error) + BroadcastGroup(context.Context, *BroadcastGroupReq) (*BroadcastGroupReply, error) + JoinGroups(context.Context, *JoinGroupsReq) (*JoinGroupsReply, error) + LeaveGroups(context.Context, *LeaveGroupsReq) (*LeaveGroupsReply, error) + DelGroups(context.Context, *DelGroupsReq) (*DelGroupsReply, error) + mustEmbedUnimplementedCometServer() +} + +// UnimplementedCometServer must be embedded to have forward compatible implementations. +type UnimplementedCometServer struct { +} + +func (UnimplementedCometServer) PushMsg(context.Context, *PushMsgReq) (*PushMsgReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method PushMsg not implemented") +} +func (UnimplementedCometServer) Broadcast(context.Context, *BroadcastReq) (*BroadcastReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method Broadcast not implemented") +} +func (UnimplementedCometServer) BroadcastGroup(context.Context, *BroadcastGroupReq) (*BroadcastGroupReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method BroadcastGroup not implemented") +} +func (UnimplementedCometServer) JoinGroups(context.Context, *JoinGroupsReq) (*JoinGroupsReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method JoinGroups not implemented") +} +func (UnimplementedCometServer) LeaveGroups(context.Context, *LeaveGroupsReq) (*LeaveGroupsReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method LeaveGroups not implemented") +} +func (UnimplementedCometServer) DelGroups(context.Context, *DelGroupsReq) (*DelGroupsReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method DelGroups not implemented") +} +func (UnimplementedCometServer) mustEmbedUnimplementedCometServer() {} + +// UnsafeCometServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to CometServer will +// result in compilation errors. +type UnsafeCometServer interface { + mustEmbedUnimplementedCometServer() +} + +func RegisterCometServer(s grpc.ServiceRegistrar, srv CometServer) { + s.RegisterService(&Comet_ServiceDesc, srv) +} + +func _Comet_PushMsg_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PushMsgReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CometServer).PushMsg(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/im.comet.Comet/PushMsg", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CometServer).PushMsg(ctx, req.(*PushMsgReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Comet_Broadcast_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(BroadcastReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CometServer).Broadcast(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/im.comet.Comet/Broadcast", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CometServer).Broadcast(ctx, req.(*BroadcastReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Comet_BroadcastGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(BroadcastGroupReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CometServer).BroadcastGroup(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/im.comet.Comet/BroadcastGroup", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CometServer).BroadcastGroup(ctx, req.(*BroadcastGroupReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Comet_JoinGroups_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(JoinGroupsReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CometServer).JoinGroups(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/im.comet.Comet/JoinGroups", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CometServer).JoinGroups(ctx, req.(*JoinGroupsReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Comet_LeaveGroups_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(LeaveGroupsReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CometServer).LeaveGroups(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/im.comet.Comet/LeaveGroups", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CometServer).LeaveGroups(ctx, req.(*LeaveGroupsReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Comet_DelGroups_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DelGroupsReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CometServer).DelGroups(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/im.comet.Comet/DelGroups", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CometServer).DelGroups(ctx, req.(*DelGroupsReq)) + } + return interceptor(ctx, in, info, handler) +} + +// Comet_ServiceDesc is the grpc.ServiceDesc for Comet service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Comet_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "im.comet.Comet", + HandlerType: (*CometServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "PushMsg", + Handler: _Comet_PushMsg_Handler, + }, + { + MethodName: "Broadcast", + Handler: _Comet_Broadcast_Handler, + }, + { + MethodName: "BroadcastGroup", + Handler: _Comet_BroadcastGroup_Handler, + }, + { + MethodName: "JoinGroups", + Handler: _Comet_JoinGroups_Handler, + }, + { + MethodName: "LeaveGroups", + Handler: _Comet_LeaveGroups_Handler, + }, + { + MethodName: "DelGroups", + Handler: _Comet_DelGroups_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "comet.proto", +} diff --git a/api/comet/grpc/create.sh b/api/comet/grpc/create.sh new file mode 100644 index 0000000..33fa63e --- /dev/null +++ b/api/comet/grpc/create.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +protoc -I. -I$GOPATH/src \ + --go_out=. --go_opt=paths=source_relative \ + --go-grpc_out=. --go-grpc_opt=paths=source_relative \ + *.proto diff --git a/api/comet/grpc/protocol.go b/api/comet/grpc/protocol.go new file mode 100644 index 0000000..15b797d --- /dev/null +++ b/api/comet/grpc/protocol.go @@ -0,0 +1,304 @@ +package grpc + +import ( + "errors" + + "github.com/Terry-Mao/goim/pkg/bufio" + "github.com/Terry-Mao/goim/pkg/bytes" + "github.com/Terry-Mao/goim/pkg/encoding/binary" + "github.com/Terry-Mao/goim/pkg/websocket" + gws "github.com/gorilla/websocket" +) + +const ( + // MaxBodySize max proto body size + MaxBodySize = int32(1 << 14) +) + +const ( + // size + _packSize = 4 + _headerSize = 2 + _verSize = 2 + _opSize = 4 + _seqSize = 4 + _ackSize = 4 + _heartSize = 4 + _rawHeaderSize = _packSize + _headerSize + _verSize + _opSize + _seqSize + _ackSize + _maxPackSize = MaxBodySize + int32(_rawHeaderSize) + // offset + _packOffset = 0 + _headerOffset = _packOffset + _packSize + _verOffset = _headerOffset + _headerSize + _opOffset = _verOffset + _verSize + _seqOffset = _opOffset + _opSize + _ackOffset = _seqOffset + _seqSize + _heartOffset = _ackOffset + _ackSize +) + +var ( + // ErrProtoPackLen proto packet len error + ErrProtoPackLen = errors.New("default server codec pack length error") + // ErrProtoHeaderLen proto header len error + ErrProtoHeaderLen = errors.New("default server codec header length error") +) + +var ( + // ProtoReady proto ready + ProtoReady = &Proto{Op: int32(Op_ProtoReady)} + // ProtoFinish proto finish + ProtoFinish = &Proto{Op: int32(Op_ProtoFinish)} +) + +// WriteTo write a proto to bytes writer. +func (p *Proto) WriteTo(b *bytes.Writer) { + var ( + packLen = _rawHeaderSize + int32(len(p.Body)) + buf = b.Peek(_rawHeaderSize) + ) + binary.BigEndian.PutInt32(buf[_packOffset:], packLen) + binary.BigEndian.PutInt16(buf[_headerOffset:], int16(_rawHeaderSize)) + binary.BigEndian.PutInt16(buf[_verOffset:], int16(p.Ver)) + binary.BigEndian.PutInt32(buf[_opOffset:], p.Op) + binary.BigEndian.PutInt32(buf[_seqOffset:], p.Seq) + binary.BigEndian.PutInt32(buf[_ackOffset:], p.Ack) + if p.Body != nil { + b.Write(p.Body) + } +} + +// ReadTCP read a proto from TCP reader. +func (p *Proto) ReadTCP(rr *bufio.Reader) (err error) { + var ( + bodyLen int + headerLen int16 + packLen int32 + buf []byte + ) + if buf, err = rr.Pop(_rawHeaderSize); err != nil { + return + } + packLen = binary.BigEndian.Int32(buf[_packOffset:_headerOffset]) + headerLen = binary.BigEndian.Int16(buf[_headerOffset:_verOffset]) + p.Ver = int32(binary.BigEndian.Int16(buf[_verOffset:_opOffset])) + p.Op = binary.BigEndian.Int32(buf[_opOffset:_seqOffset]) + p.Seq = binary.BigEndian.Int32(buf[_seqOffset:_ackOffset]) + p.Ack = binary.BigEndian.Int32(buf[_ackOffset:]) + if packLen > _maxPackSize { + return ErrProtoPackLen + } + if headerLen != _rawHeaderSize { + return ErrProtoHeaderLen + } + if bodyLen = int(packLen - int32(headerLen)); bodyLen > 0 { + p.Body, err = rr.Pop(bodyLen) + } else { + p.Body = nil + } + return +} + +// WriteTCP write a proto to TCP writer. +func (p *Proto) WriteTCP(wr *bufio.Writer) (err error) { + var ( + buf []byte + packLen int32 + ) + if p.Op == int32(Op_Raw) { + // write without buffer, job concact proto into raw buffer + _, err = wr.WriteRaw(p.Body) + return + } + packLen = _rawHeaderSize + int32(len(p.Body)) + if buf, err = wr.Peek(_rawHeaderSize); err != nil { + return + } + binary.BigEndian.PutInt32(buf[_packOffset:], packLen) + binary.BigEndian.PutInt16(buf[_headerOffset:], int16(_rawHeaderSize)) + binary.BigEndian.PutInt16(buf[_verOffset:], int16(p.Ver)) + binary.BigEndian.PutInt32(buf[_opOffset:], p.Op) + binary.BigEndian.PutInt32(buf[_seqOffset:], p.Seq) + binary.BigEndian.PutInt32(buf[_ackOffset:], p.Ack) + if p.Body != nil { + _, err = wr.Write(p.Body) + } + return +} + +// WriteTCPHeart write TCP heartbeat with room online. +func (p *Proto) WriteTCPHeart(wr *bufio.Writer, online int32) (err error) { + var ( + buf []byte + packLen int + ) + packLen = _rawHeaderSize + _heartSize + if buf, err = wr.Peek(packLen); err != nil { + return + } + // header + binary.BigEndian.PutInt32(buf[_packOffset:], int32(packLen)) + binary.BigEndian.PutInt16(buf[_headerOffset:], int16(_rawHeaderSize)) + binary.BigEndian.PutInt16(buf[_verOffset:], int16(p.Ver)) + binary.BigEndian.PutInt32(buf[_opOffset:], p.Op) + binary.BigEndian.PutInt32(buf[_seqOffset:], p.Seq) + binary.BigEndian.PutInt32(buf[_ackOffset:], p.Ack) + // body + binary.BigEndian.PutInt32(buf[_heartOffset:], online) + return +} + +// ReadWebsocket read a proto from websocket connection. +func (p *Proto) ReadWebsocket(ws *websocket.Conn) (err error) { + var ( + bodyLen int + headerLen int16 + packLen int32 + buf []byte + ) + if _, buf, err = ws.ReadMessage(); err != nil { + return + } + if len(buf) < _rawHeaderSize { + return ErrProtoPackLen + } + packLen = binary.BigEndian.Int32(buf[_packOffset:_headerOffset]) + headerLen = binary.BigEndian.Int16(buf[_headerOffset:_verOffset]) + p.Ver = int32(binary.BigEndian.Int16(buf[_verOffset:_opOffset])) + p.Op = binary.BigEndian.Int32(buf[_opOffset:_seqOffset]) + p.Seq = binary.BigEndian.Int32(buf[_seqOffset:_ackOffset]) + p.Ack = binary.BigEndian.Int32(buf[_ackOffset:]) + if packLen < 0 || packLen > _maxPackSize { + return ErrProtoPackLen + } + if headerLen != _rawHeaderSize { + return ErrProtoHeaderLen + } + if bodyLen = int(packLen - int32(headerLen)); bodyLen > 0 { + p.Body = buf[headerLen:packLen] + } else { + p.Body = nil + } + return +} + +// WriteWebsocket write a proto to websocket connection. +func (p *Proto) WriteWebsocket(ws *websocket.Conn) (err error) { + var ( + buf []byte + packLen int + ) + packLen = _rawHeaderSize + len(p.Body) + if err = ws.WriteHeader(websocket.BinaryMessage, packLen); err != nil { + return + } + if buf, err = ws.Peek(_rawHeaderSize); err != nil { + return + } + binary.BigEndian.PutInt32(buf[_packOffset:], int32(packLen)) + binary.BigEndian.PutInt16(buf[_headerOffset:], int16(_rawHeaderSize)) + binary.BigEndian.PutInt16(buf[_verOffset:], int16(p.Ver)) + binary.BigEndian.PutInt32(buf[_opOffset:], p.Op) + binary.BigEndian.PutInt32(buf[_seqOffset:], p.Seq) + binary.BigEndian.PutInt32(buf[_ackOffset:], p.Ack) + if p.Body != nil { + err = ws.WriteBody(p.Body) + } + return +} + +// WriteWebsocketHeart write websocket heartbeat with room online. +func (p *Proto) WriteWebsocketHeart(wr *websocket.Conn, online int32) (err error) { + var ( + buf []byte + packLen int + ) + packLen = _rawHeaderSize + _heartSize + // websocket header + if err = wr.WriteHeader(websocket.BinaryMessage, packLen); err != nil { + return + } + if buf, err = wr.Peek(packLen); err != nil { + return + } + // proto header + binary.BigEndian.PutInt32(buf[_packOffset:], int32(packLen)) + binary.BigEndian.PutInt16(buf[_headerOffset:], int16(_rawHeaderSize)) + binary.BigEndian.PutInt16(buf[_verOffset:], int16(p.Ver)) + binary.BigEndian.PutInt32(buf[_opOffset:], p.Op) + binary.BigEndian.PutInt32(buf[_seqOffset:], p.Seq) + binary.BigEndian.PutInt32(buf[_ackOffset:], p.Ack) + // proto body + binary.BigEndian.PutInt32(buf[_heartOffset:], online) + return +} + +// WriteWebsocket write a proto to websocket connection. +func (p *Proto) WriteWebsocket2(ws *gws.Conn) (err error) { + var ( + buf []byte + packLen int32 + ) + + wc, err := ws.NextWriter(websocket.BinaryMessage) + if err != nil { + return err + } + + wr := bufio.NewWriter(wc) + packLen = _rawHeaderSize + int32(len(p.Body)) + if buf, err = wr.Peek(_rawHeaderSize); err != nil { + return + } + + binary.BigEndian.PutInt32(buf[_packOffset:], packLen) + binary.BigEndian.PutInt16(buf[_headerOffset:], int16(_rawHeaderSize)) + binary.BigEndian.PutInt16(buf[_verOffset:], int16(p.Ver)) + binary.BigEndian.PutInt32(buf[_opOffset:], p.Op) + binary.BigEndian.PutInt32(buf[_seqOffset:], p.Seq) + binary.BigEndian.PutInt32(buf[_ackOffset:], p.Ack) + if p.Body != nil { + _, err = wr.Write(p.Body) + } + if err != nil { + return + } + err = wr.Flush() + if err != nil { + return + } + return wc.Close() +} + +// ReadWebsocket read a proto from websocket connection. +func (p *Proto) ReadWebsocket2(ws *gws.Conn) (err error) { + var ( + bodyLen int + headerLen int16 + packLen int32 + buf []byte + ) + if _, buf, err = ws.ReadMessage(); err != nil { + return + } + if len(buf) < _rawHeaderSize { + return ErrProtoPackLen + } + packLen = binary.BigEndian.Int32(buf[_packOffset:_headerOffset]) + headerLen = binary.BigEndian.Int16(buf[_headerOffset:_verOffset]) + p.Ver = int32(binary.BigEndian.Int16(buf[_verOffset:_opOffset])) + p.Op = binary.BigEndian.Int32(buf[_opOffset:_seqOffset]) + p.Seq = binary.BigEndian.Int32(buf[_seqOffset:_ackOffset]) + p.Ack = binary.BigEndian.Int32(buf[_ackOffset:]) + if packLen > _maxPackSize { + return ErrProtoPackLen + } + if headerLen != _rawHeaderSize { + return ErrProtoHeaderLen + } + if bodyLen = int(packLen - int32(headerLen)); bodyLen > 0 { + p.Body = buf[headerLen:packLen] + } else { + p.Body = nil + } + return +} diff --git a/api/logic/grpc/create.sh b/api/logic/grpc/create.sh new file mode 100644 index 0000000..33fa63e --- /dev/null +++ b/api/logic/grpc/create.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +protoc -I. -I$GOPATH/src \ + --go_out=. --go_opt=paths=source_relative \ + --go-grpc_out=. --go-grpc_opt=paths=source_relative \ + *.proto diff --git a/api/logic/grpc/logic.pb.go b/api/logic/grpc/logic.pb.go new file mode 100644 index 0000000..143b2b2 --- /dev/null +++ b/api/logic/grpc/logic.pb.go @@ -0,0 +1,1312 @@ +// protoc -I=. -I=$GOPATH/src --go_out=plugins=grpc:. *.proto + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.19.1 +// source: logic.proto + +package grpc + +import ( + grpc "gitlab.33.cn/chat/im/api/comet/grpc" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Type int32 + +const ( + Type_PUSH Type = 0 + Type_ROOM Type = 1 + Type_BROADCAST Type = 2 +) + +// Enum value maps for Type. +var ( + Type_name = map[int32]string{ + 0: "PUSH", + 1: "ROOM", + 2: "BROADCAST", + } + Type_value = map[string]int32{ + "PUSH": 0, + "ROOM": 1, + "BROADCAST": 2, + } +) + +func (x Type) Enum() *Type { + p := new(Type) + *p = x + return p +} + +func (x Type) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Type) Descriptor() protoreflect.EnumDescriptor { + return file_logic_proto_enumTypes[0].Descriptor() +} + +func (Type) Type() protoreflect.EnumType { + return &file_logic_proto_enumTypes[0] +} + +func (x Type) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Type.Descriptor instead. +func (Type) EnumDescriptor() ([]byte, []int) { + return file_logic_proto_rawDescGZIP(), []int{0} +} + +type ConnectReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Server string `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"` // 客户端连接的是哪个 comet + Proto *grpc.Proto `protobuf:"bytes,3,opt,name=proto,proto3" json:"proto,omitempty"` +} + +func (x *ConnectReq) Reset() { + *x = ConnectReq{} + if protoimpl.UnsafeEnabled { + mi := &file_logic_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConnectReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConnectReq) ProtoMessage() {} + +func (x *ConnectReq) ProtoReflect() protoreflect.Message { + mi := &file_logic_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConnectReq.ProtoReflect.Descriptor instead. +func (*ConnectReq) Descriptor() ([]byte, []int) { + return file_logic_proto_rawDescGZIP(), []int{0} +} + +func (x *ConnectReq) GetServer() string { + if x != nil { + return x.Server + } + return "" +} + +func (x *ConnectReq) GetProto() *grpc.Proto { + if x != nil { + return x.Proto + } + return nil +} + +type ConnectReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + AppId string `protobuf:"bytes,2,opt,name=appId,proto3" json:"appId,omitempty"` + Mid string `protobuf:"bytes,3,opt,name=mid,proto3" json:"mid,omitempty"` + Heartbeat int64 `protobuf:"varint,4,opt,name=heartbeat,proto3" json:"heartbeat,omitempty"` +} + +func (x *ConnectReply) Reset() { + *x = ConnectReply{} + if protoimpl.UnsafeEnabled { + mi := &file_logic_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConnectReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConnectReply) ProtoMessage() {} + +func (x *ConnectReply) ProtoReflect() protoreflect.Message { + mi := &file_logic_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConnectReply.ProtoReflect.Descriptor instead. +func (*ConnectReply) Descriptor() ([]byte, []int) { + return file_logic_proto_rawDescGZIP(), []int{1} +} + +func (x *ConnectReply) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *ConnectReply) GetAppId() string { + if x != nil { + return x.AppId + } + return "" +} + +func (x *ConnectReply) GetMid() string { + if x != nil { + return x.Mid + } + return "" +} + +func (x *ConnectReply) GetHeartbeat() int64 { + if x != nil { + return x.Heartbeat + } + return 0 +} + +type DisconnectReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Server string `protobuf:"bytes,2,opt,name=server,proto3" json:"server,omitempty"` +} + +func (x *DisconnectReq) Reset() { + *x = DisconnectReq{} + if protoimpl.UnsafeEnabled { + mi := &file_logic_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DisconnectReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DisconnectReq) ProtoMessage() {} + +func (x *DisconnectReq) ProtoReflect() protoreflect.Message { + mi := &file_logic_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DisconnectReq.ProtoReflect.Descriptor instead. +func (*DisconnectReq) Descriptor() ([]byte, []int) { + return file_logic_proto_rawDescGZIP(), []int{2} +} + +func (x *DisconnectReq) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *DisconnectReq) GetServer() string { + if x != nil { + return x.Server + } + return "" +} + +type HeartbeatReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Server string `protobuf:"bytes,2,opt,name=server,proto3" json:"server,omitempty"` +} + +func (x *HeartbeatReq) Reset() { + *x = HeartbeatReq{} + if protoimpl.UnsafeEnabled { + mi := &file_logic_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HeartbeatReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HeartbeatReq) ProtoMessage() {} + +func (x *HeartbeatReq) ProtoReflect() protoreflect.Message { + mi := &file_logic_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HeartbeatReq.ProtoReflect.Descriptor instead. +func (*HeartbeatReq) Descriptor() ([]byte, []int) { + return file_logic_proto_rawDescGZIP(), []int{3} +} + +func (x *HeartbeatReq) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *HeartbeatReq) GetServer() string { + if x != nil { + return x.Server + } + return "" +} + +type ReceiveReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Proto *grpc.Proto `protobuf:"bytes,3,opt,name=proto,proto3" json:"proto,omitempty"` +} + +func (x *ReceiveReq) Reset() { + *x = ReceiveReq{} + if protoimpl.UnsafeEnabled { + mi := &file_logic_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReceiveReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReceiveReq) ProtoMessage() {} + +func (x *ReceiveReq) ProtoReflect() protoreflect.Message { + mi := &file_logic_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReceiveReq.ProtoReflect.Descriptor instead. +func (*ReceiveReq) Descriptor() ([]byte, []int) { + return file_logic_proto_rawDescGZIP(), []int{4} +} + +func (x *ReceiveReq) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *ReceiveReq) GetProto() *grpc.Proto { + if x != nil { + return x.Proto + } + return nil +} + +type Reply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + IsOk bool `protobuf:"varint,1,opt,name=isOk,proto3" json:"isOk,omitempty"` + Msg []byte `protobuf:"bytes,2,opt,name=msg,proto3" json:"msg,omitempty"` +} + +func (x *Reply) Reset() { + *x = Reply{} + if protoimpl.UnsafeEnabled { + mi := &file_logic_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Reply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Reply) ProtoMessage() {} + +func (x *Reply) ProtoReflect() protoreflect.Message { + mi := &file_logic_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Reply.ProtoReflect.Descriptor instead. +func (*Reply) Descriptor() ([]byte, []int) { + return file_logic_proto_rawDescGZIP(), []int{5} +} + +func (x *Reply) GetIsOk() bool { + if x != nil { + return x.IsOk + } + return false +} + +func (x *Reply) GetMsg() []byte { + if x != nil { + return x.Msg + } + return nil +} + +// logic --> mq +type BizMsg struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AppId string `protobuf:"bytes,1,opt,name=appId,proto3" json:"appId,omitempty"` + FromId string `protobuf:"bytes,2,opt,name=fromId,proto3" json:"fromId,omitempty"` + Type Type `protobuf:"varint,4,opt,name=type,proto3,enum=im.logic.Type" json:"type,omitempty"` + Op int32 `protobuf:"varint,5,opt,name=op,proto3" json:"op,omitempty"` + Key string `protobuf:"bytes,6,opt,name=key,proto3" json:"key,omitempty"` + Msg []byte `protobuf:"bytes,7,opt,name=msg,proto3" json:"msg,omitempty"` +} + +func (x *BizMsg) Reset() { + *x = BizMsg{} + if protoimpl.UnsafeEnabled { + mi := &file_logic_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BizMsg) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BizMsg) ProtoMessage() {} + +func (x *BizMsg) ProtoReflect() protoreflect.Message { + mi := &file_logic_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BizMsg.ProtoReflect.Descriptor instead. +func (*BizMsg) Descriptor() ([]byte, []int) { + return file_logic_proto_rawDescGZIP(), []int{6} +} + +func (x *BizMsg) GetAppId() string { + if x != nil { + return x.AppId + } + return "" +} + +func (x *BizMsg) GetFromId() string { + if x != nil { + return x.FromId + } + return "" +} + +func (x *BizMsg) GetType() Type { + if x != nil { + return x.Type + } + return Type_PUSH +} + +func (x *BizMsg) GetOp() int32 { + if x != nil { + return x.Op + } + return 0 +} + +func (x *BizMsg) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *BizMsg) GetMsg() []byte { + if x != nil { + return x.Msg + } + return nil +} + +// biz --> logic +type MidsMsg struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AppId string `protobuf:"bytes,1,opt,name=appId,proto3" json:"appId,omitempty"` + ToIds []string `protobuf:"bytes,2,rep,name=toIds,proto3" json:"toIds,omitempty"` + Type Type `protobuf:"varint,3,opt,name=type,proto3,enum=im.logic.Type" json:"type,omitempty"` + Op int32 `protobuf:"varint,4,opt,name=op,proto3" json:"op,omitempty"` + Msg []byte `protobuf:"bytes,5,opt,name=msg,proto3" json:"msg,omitempty"` +} + +func (x *MidsMsg) Reset() { + *x = MidsMsg{} + if protoimpl.UnsafeEnabled { + mi := &file_logic_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MidsMsg) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MidsMsg) ProtoMessage() {} + +func (x *MidsMsg) ProtoReflect() protoreflect.Message { + mi := &file_logic_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MidsMsg.ProtoReflect.Descriptor instead. +func (*MidsMsg) Descriptor() ([]byte, []int) { + return file_logic_proto_rawDescGZIP(), []int{7} +} + +func (x *MidsMsg) GetAppId() string { + if x != nil { + return x.AppId + } + return "" +} + +func (x *MidsMsg) GetToIds() []string { + if x != nil { + return x.ToIds + } + return nil +} + +func (x *MidsMsg) GetType() Type { + if x != nil { + return x.Type + } + return Type_PUSH +} + +func (x *MidsMsg) GetOp() int32 { + if x != nil { + return x.Op + } + return 0 +} + +func (x *MidsMsg) GetMsg() []byte { + if x != nil { + return x.Msg + } + return nil +} + +// biz --> logic +type KeysMsg struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AppId string `protobuf:"bytes,1,opt,name=appId,proto3" json:"appId,omitempty"` + ToKeys []string `protobuf:"bytes,2,rep,name=toKeys,proto3" json:"toKeys,omitempty"` + Type Type `protobuf:"varint,3,opt,name=type,proto3,enum=im.logic.Type" json:"type,omitempty"` + Op int32 `protobuf:"varint,4,opt,name=op,proto3" json:"op,omitempty"` + Msg []byte `protobuf:"bytes,5,opt,name=msg,proto3" json:"msg,omitempty"` +} + +func (x *KeysMsg) Reset() { + *x = KeysMsg{} + if protoimpl.UnsafeEnabled { + mi := &file_logic_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *KeysMsg) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*KeysMsg) ProtoMessage() {} + +func (x *KeysMsg) ProtoReflect() protoreflect.Message { + mi := &file_logic_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use KeysMsg.ProtoReflect.Descriptor instead. +func (*KeysMsg) Descriptor() ([]byte, []int) { + return file_logic_proto_rawDescGZIP(), []int{8} +} + +func (x *KeysMsg) GetAppId() string { + if x != nil { + return x.AppId + } + return "" +} + +func (x *KeysMsg) GetToKeys() []string { + if x != nil { + return x.ToKeys + } + return nil +} + +func (x *KeysMsg) GetType() Type { + if x != nil { + return x.Type + } + return Type_PUSH +} + +func (x *KeysMsg) GetOp() int32 { + if x != nil { + return x.Op + } + return 0 +} + +func (x *KeysMsg) GetMsg() []byte { + if x != nil { + return x.Msg + } + return nil +} + +// biz --> logic +type GroupMsg struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AppId string `protobuf:"bytes,1,opt,name=appId,proto3" json:"appId,omitempty"` + Group string `protobuf:"bytes,2,opt,name=group,proto3" json:"group,omitempty"` + Type Type `protobuf:"varint,3,opt,name=type,proto3,enum=im.logic.Type" json:"type,omitempty"` + Op int32 `protobuf:"varint,4,opt,name=op,proto3" json:"op,omitempty"` + Msg []byte `protobuf:"bytes,5,opt,name=msg,proto3" json:"msg,omitempty"` +} + +func (x *GroupMsg) Reset() { + *x = GroupMsg{} + if protoimpl.UnsafeEnabled { + mi := &file_logic_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GroupMsg) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GroupMsg) ProtoMessage() {} + +func (x *GroupMsg) ProtoReflect() protoreflect.Message { + mi := &file_logic_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GroupMsg.ProtoReflect.Descriptor instead. +func (*GroupMsg) Descriptor() ([]byte, []int) { + return file_logic_proto_rawDescGZIP(), []int{9} +} + +func (x *GroupMsg) GetAppId() string { + if x != nil { + return x.AppId + } + return "" +} + +func (x *GroupMsg) GetGroup() string { + if x != nil { + return x.Group + } + return "" +} + +func (x *GroupMsg) GetType() Type { + if x != nil { + return x.Type + } + return Type_PUSH +} + +func (x *GroupMsg) GetOp() int32 { + if x != nil { + return x.Op + } + return 0 +} + +func (x *GroupMsg) GetMsg() []byte { + if x != nil { + return x.Msg + } + return nil +} + +// biz --> logic +type GroupsKey struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AppId string `protobuf:"bytes,1,opt,name=appId,proto3" json:"appId,omitempty"` + Keys []string `protobuf:"bytes,2,rep,name=keys,proto3" json:"keys,omitempty"` + Gid []string `protobuf:"bytes,3,rep,name=gid,proto3" json:"gid,omitempty"` +} + +func (x *GroupsKey) Reset() { + *x = GroupsKey{} + if protoimpl.UnsafeEnabled { + mi := &file_logic_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GroupsKey) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GroupsKey) ProtoMessage() {} + +func (x *GroupsKey) ProtoReflect() protoreflect.Message { + mi := &file_logic_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GroupsKey.ProtoReflect.Descriptor instead. +func (*GroupsKey) Descriptor() ([]byte, []int) { + return file_logic_proto_rawDescGZIP(), []int{10} +} + +func (x *GroupsKey) GetAppId() string { + if x != nil { + return x.AppId + } + return "" +} + +func (x *GroupsKey) GetKeys() []string { + if x != nil { + return x.Keys + } + return nil +} + +func (x *GroupsKey) GetGid() []string { + if x != nil { + return x.Gid + } + return nil +} + +// biz --> logic +type GroupsMid struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AppId string `protobuf:"bytes,1,opt,name=appId,proto3" json:"appId,omitempty"` + Mids []string `protobuf:"bytes,2,rep,name=mids,proto3" json:"mids,omitempty"` + Gid []string `protobuf:"bytes,3,rep,name=gid,proto3" json:"gid,omitempty"` +} + +func (x *GroupsMid) Reset() { + *x = GroupsMid{} + if protoimpl.UnsafeEnabled { + mi := &file_logic_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GroupsMid) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GroupsMid) ProtoMessage() {} + +func (x *GroupsMid) ProtoReflect() protoreflect.Message { + mi := &file_logic_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GroupsMid.ProtoReflect.Descriptor instead. +func (*GroupsMid) Descriptor() ([]byte, []int) { + return file_logic_proto_rawDescGZIP(), []int{11} +} + +func (x *GroupsMid) GetAppId() string { + if x != nil { + return x.AppId + } + return "" +} + +func (x *GroupsMid) GetMids() []string { + if x != nil { + return x.Mids + } + return nil +} + +func (x *GroupsMid) GetGid() []string { + if x != nil { + return x.Gid + } + return nil +} + +// biz --> logic +type DelGroupsReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AppId string `protobuf:"bytes,1,opt,name=appId,proto3" json:"appId,omitempty"` + Gid []string `protobuf:"bytes,2,rep,name=gid,proto3" json:"gid,omitempty"` +} + +func (x *DelGroupsReq) Reset() { + *x = DelGroupsReq{} + if protoimpl.UnsafeEnabled { + mi := &file_logic_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DelGroupsReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DelGroupsReq) ProtoMessage() {} + +func (x *DelGroupsReq) ProtoReflect() protoreflect.Message { + mi := &file_logic_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DelGroupsReq.ProtoReflect.Descriptor instead. +func (*DelGroupsReq) Descriptor() ([]byte, []int) { + return file_logic_proto_rawDescGZIP(), []int{12} +} + +func (x *DelGroupsReq) GetAppId() string { + if x != nil { + return x.AppId + } + return "" +} + +func (x *DelGroupsReq) GetGid() []string { + if x != nil { + return x.Gid + } + return nil +} + +var File_logic_proto protoreflect.FileDescriptor + +var file_logic_proto_rawDesc = []byte{ + 0x0a, 0x0b, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x69, + 0x6d, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x1a, 0x2f, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, + 0x33, 0x33, 0x2e, 0x63, 0x6e, 0x2f, 0x63, 0x68, 0x61, 0x74, 0x2f, 0x69, 0x6d, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x6f, 0x6d, + 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x4b, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x25, + 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, + 0x69, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x05, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x66, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x49, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x70, 0x70, 0x49, 0x64, 0x12, 0x10, 0x0a, + 0x03, 0x6d, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x69, 0x64, 0x12, + 0x1c, 0x0a, 0x09, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x09, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x22, 0x39, 0x0a, + 0x0d, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x38, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x72, + 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65, 0x71, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x22, 0x45, 0x0a, 0x0a, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x52, 0x65, 0x71, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x25, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0f, 0x2e, 0x69, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x65, 0x74, 0x2e, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x2d, 0x0a, 0x05, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x73, 0x4f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x04, 0x69, 0x73, 0x4f, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x22, 0x8e, 0x01, 0x0a, 0x06, 0x42, 0x69, 0x7a, + 0x4d, 0x73, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x61, 0x70, 0x70, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x72, 0x6f, + 0x6d, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x72, 0x6f, 0x6d, 0x49, + 0x64, 0x12, 0x22, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x0e, 0x2e, 0x69, 0x6d, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x02, 0x6f, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x22, 0x7b, 0x0a, 0x07, 0x4d, 0x69, 0x64, + 0x73, 0x4d, 0x73, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x49, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x70, 0x70, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, + 0x49, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x49, 0x64, 0x73, + 0x12, 0x22, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, + 0x2e, 0x69, 0x6d, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x02, 0x6f, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x22, 0x7d, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x73, 0x4d, 0x73, + 0x67, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x61, 0x70, 0x70, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x6f, 0x4b, 0x65, 0x79, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x74, 0x6f, 0x4b, 0x65, 0x79, 0x73, 0x12, + 0x22, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, + 0x69, 0x6d, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x02, 0x6f, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x03, 0x6d, 0x73, 0x67, 0x22, 0x7c, 0x0a, 0x08, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x73, + 0x67, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x61, 0x70, 0x70, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x22, 0x0a, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x69, 0x6d, + 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x6f, + 0x70, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, + 0x6d, 0x73, 0x67, 0x22, 0x47, 0x0a, 0x09, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x4b, 0x65, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x61, 0x70, 0x70, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x69, + 0x64, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x67, 0x69, 0x64, 0x22, 0x47, 0x0a, 0x09, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x4d, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x70, 0x70, + 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x70, 0x70, 0x49, 0x64, 0x12, + 0x12, 0x0a, 0x04, 0x6d, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x6d, + 0x69, 0x64, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x69, 0x64, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x03, 0x67, 0x69, 0x64, 0x22, 0x36, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x73, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x49, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x70, 0x70, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x67, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x67, 0x69, 0x64, 0x2a, 0x29, 0x0a, + 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x50, 0x55, 0x53, 0x48, 0x10, 0x00, 0x12, + 0x08, 0x0a, 0x04, 0x52, 0x4f, 0x4f, 0x4d, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x42, 0x52, 0x4f, + 0x41, 0x44, 0x43, 0x41, 0x53, 0x54, 0x10, 0x02, 0x32, 0x96, 0x05, 0x0a, 0x05, 0x4c, 0x6f, 0x67, + 0x69, 0x63, 0x12, 0x37, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x12, 0x14, 0x2e, + 0x69, 0x6d, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x69, 0x6d, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x43, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x36, 0x0a, 0x0a, 0x44, + 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x12, 0x17, 0x2e, 0x69, 0x6d, 0x2e, 0x6c, + 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, + 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x69, 0x6d, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x52, 0x65, + 0x70, 0x6c, 0x79, 0x12, 0x34, 0x0a, 0x09, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, + 0x12, 0x16, 0x2e, 0x69, 0x6d, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x48, 0x65, 0x61, 0x72, + 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x69, 0x6d, 0x2e, 0x6c, 0x6f, + 0x67, 0x69, 0x63, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x30, 0x0a, 0x07, 0x52, 0x65, 0x63, + 0x65, 0x69, 0x76, 0x65, 0x12, 0x14, 0x2e, 0x69, 0x6d, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x2e, + 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x69, 0x6d, 0x2e, + 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x30, 0x0a, 0x0a, 0x50, + 0x75, 0x73, 0x68, 0x42, 0x79, 0x4d, 0x69, 0x64, 0x73, 0x12, 0x11, 0x2e, 0x69, 0x6d, 0x2e, 0x6c, + 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x4d, 0x69, 0x64, 0x73, 0x4d, 0x73, 0x67, 0x1a, 0x0f, 0x2e, 0x69, + 0x6d, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x30, 0x0a, + 0x0a, 0x50, 0x75, 0x73, 0x68, 0x42, 0x79, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x11, 0x2e, 0x69, 0x6d, + 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x4d, 0x73, 0x67, 0x1a, 0x0f, + 0x2e, 0x69, 0x6d, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, + 0x30, 0x0a, 0x09, 0x50, 0x75, 0x73, 0x68, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x12, 0x2e, 0x69, + 0x6d, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x73, 0x67, + 0x1a, 0x0f, 0x2e, 0x69, 0x6d, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x12, 0x38, 0x0a, 0x10, 0x4a, 0x6f, 0x69, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x42, + 0x79, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x13, 0x2e, 0x69, 0x6d, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, + 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x4b, 0x65, 0x79, 0x1a, 0x0f, 0x2e, 0x69, 0x6d, 0x2e, + 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x38, 0x0a, 0x10, 0x4a, + 0x6f, 0x69, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x42, 0x79, 0x4d, 0x69, 0x64, 0x73, 0x12, + 0x13, 0x2e, 0x69, 0x6d, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x73, 0x4d, 0x69, 0x64, 0x1a, 0x0f, 0x2e, 0x69, 0x6d, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x2e, + 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x39, 0x0a, 0x11, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x73, 0x42, 0x79, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x13, 0x2e, 0x69, 0x6d, 0x2e, + 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x4b, 0x65, 0x79, 0x1a, + 0x0f, 0x2e, 0x69, 0x6d, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, + 0x12, 0x39, 0x0a, 0x11, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x42, + 0x79, 0x4d, 0x69, 0x64, 0x73, 0x12, 0x13, 0x2e, 0x69, 0x6d, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, + 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x4d, 0x69, 0x64, 0x1a, 0x0f, 0x2e, 0x69, 0x6d, 0x2e, + 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x34, 0x0a, 0x09, 0x44, + 0x65, 0x6c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x16, 0x2e, 0x69, 0x6d, 0x2e, 0x6c, 0x6f, + 0x67, 0x69, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, + 0x1a, 0x0f, 0x2e, 0x69, 0x6d, 0x2e, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x2e, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x42, 0x25, 0x5a, 0x23, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x33, 0x33, 0x2e, 0x63, + 0x6e, 0x2f, 0x63, 0x68, 0x61, 0x74, 0x2f, 0x69, 0x6d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6c, 0x6f, + 0x67, 0x69, 0x63, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_logic_proto_rawDescOnce sync.Once + file_logic_proto_rawDescData = file_logic_proto_rawDesc +) + +func file_logic_proto_rawDescGZIP() []byte { + file_logic_proto_rawDescOnce.Do(func() { + file_logic_proto_rawDescData = protoimpl.X.CompressGZIP(file_logic_proto_rawDescData) + }) + return file_logic_proto_rawDescData +} + +var file_logic_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_logic_proto_msgTypes = make([]protoimpl.MessageInfo, 13) +var file_logic_proto_goTypes = []interface{}{ + (Type)(0), // 0: im.logic.Type + (*ConnectReq)(nil), // 1: im.logic.ConnectReq + (*ConnectReply)(nil), // 2: im.logic.ConnectReply + (*DisconnectReq)(nil), // 3: im.logic.DisconnectReq + (*HeartbeatReq)(nil), // 4: im.logic.HeartbeatReq + (*ReceiveReq)(nil), // 5: im.logic.ReceiveReq + (*Reply)(nil), // 6: im.logic.Reply + (*BizMsg)(nil), // 7: im.logic.BizMsg + (*MidsMsg)(nil), // 8: im.logic.MidsMsg + (*KeysMsg)(nil), // 9: im.logic.KeysMsg + (*GroupMsg)(nil), // 10: im.logic.GroupMsg + (*GroupsKey)(nil), // 11: im.logic.GroupsKey + (*GroupsMid)(nil), // 12: im.logic.GroupsMid + (*DelGroupsReq)(nil), // 13: im.logic.DelGroupsReq + (*grpc.Proto)(nil), // 14: im.comet.Proto +} +var file_logic_proto_depIdxs = []int32{ + 14, // 0: im.logic.ConnectReq.proto:type_name -> im.comet.Proto + 14, // 1: im.logic.ReceiveReq.proto:type_name -> im.comet.Proto + 0, // 2: im.logic.BizMsg.type:type_name -> im.logic.Type + 0, // 3: im.logic.MidsMsg.type:type_name -> im.logic.Type + 0, // 4: im.logic.KeysMsg.type:type_name -> im.logic.Type + 0, // 5: im.logic.GroupMsg.type:type_name -> im.logic.Type + 1, // 6: im.logic.Logic.Connect:input_type -> im.logic.ConnectReq + 3, // 7: im.logic.Logic.Disconnect:input_type -> im.logic.DisconnectReq + 4, // 8: im.logic.Logic.Heartbeat:input_type -> im.logic.HeartbeatReq + 5, // 9: im.logic.Logic.Receive:input_type -> im.logic.ReceiveReq + 8, // 10: im.logic.Logic.PushByMids:input_type -> im.logic.MidsMsg + 9, // 11: im.logic.Logic.PushByKeys:input_type -> im.logic.KeysMsg + 10, // 12: im.logic.Logic.PushGroup:input_type -> im.logic.GroupMsg + 11, // 13: im.logic.Logic.JoinGroupsByKeys:input_type -> im.logic.GroupsKey + 12, // 14: im.logic.Logic.JoinGroupsByMids:input_type -> im.logic.GroupsMid + 11, // 15: im.logic.Logic.LeaveGroupsByKeys:input_type -> im.logic.GroupsKey + 12, // 16: im.logic.Logic.LeaveGroupsByMids:input_type -> im.logic.GroupsMid + 13, // 17: im.logic.Logic.DelGroups:input_type -> im.logic.DelGroupsReq + 2, // 18: im.logic.Logic.Connect:output_type -> im.logic.ConnectReply + 6, // 19: im.logic.Logic.Disconnect:output_type -> im.logic.Reply + 6, // 20: im.logic.Logic.Heartbeat:output_type -> im.logic.Reply + 6, // 21: im.logic.Logic.Receive:output_type -> im.logic.Reply + 6, // 22: im.logic.Logic.PushByMids:output_type -> im.logic.Reply + 6, // 23: im.logic.Logic.PushByKeys:output_type -> im.logic.Reply + 6, // 24: im.logic.Logic.PushGroup:output_type -> im.logic.Reply + 6, // 25: im.logic.Logic.JoinGroupsByKeys:output_type -> im.logic.Reply + 6, // 26: im.logic.Logic.JoinGroupsByMids:output_type -> im.logic.Reply + 6, // 27: im.logic.Logic.LeaveGroupsByKeys:output_type -> im.logic.Reply + 6, // 28: im.logic.Logic.LeaveGroupsByMids:output_type -> im.logic.Reply + 6, // 29: im.logic.Logic.DelGroups:output_type -> im.logic.Reply + 18, // [18:30] is the sub-list for method output_type + 6, // [6:18] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_logic_proto_init() } +func file_logic_proto_init() { + if File_logic_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_logic_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConnectReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_logic_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConnectReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_logic_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DisconnectReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_logic_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HeartbeatReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_logic_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReceiveReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_logic_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Reply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_logic_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BizMsg); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_logic_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MidsMsg); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_logic_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*KeysMsg); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_logic_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GroupMsg); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_logic_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GroupsKey); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_logic_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GroupsMid); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_logic_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DelGroupsReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_logic_proto_rawDesc, + NumEnums: 1, + NumMessages: 13, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_logic_proto_goTypes, + DependencyIndexes: file_logic_proto_depIdxs, + EnumInfos: file_logic_proto_enumTypes, + MessageInfos: file_logic_proto_msgTypes, + }.Build() + File_logic_proto = out.File + file_logic_proto_rawDesc = nil + file_logic_proto_goTypes = nil + file_logic_proto_depIdxs = nil +} diff --git a/api/logic/grpc/logic.proto b/api/logic/grpc/logic.proto new file mode 100644 index 0000000..77b2590 --- /dev/null +++ b/api/logic/grpc/logic.proto @@ -0,0 +1,117 @@ +// protoc -I=. -I=$GOPATH/src --go_out=plugins=grpc:. *.proto +syntax = "proto3"; + +package im.logic; +option go_package = "gitlab.33.cn/chat/im/api/logic/grpc"; + +import "gitlab.33.cn/chat/im/api/comet/grpc/comet.proto"; + +enum Type { + PUSH = 0; + ROOM = 1; + BROADCAST = 2; +} + +message ConnectReq { + string server = 1; // 客户端连接的是哪个 comet + im.comet.Proto proto = 3; +} + +message ConnectReply { + string key = 1; + string appId = 2; + string mid = 3; + int64 heartbeat = 4; +} + +message DisconnectReq { + string key = 1; + string server = 2; +} + +message HeartbeatReq { + string key = 1; + string server = 2; +} + +message ReceiveReq { + string key = 1; + im.comet.Proto proto = 3; +} + +message Reply { + bool isOk = 1; + bytes msg = 2; +} + +// logic --> mq +message BizMsg { + string appId = 1; + string fromId = 2; + Type type = 4; + int32 op = 5; + string key = 6; + bytes msg = 7; +} + +// biz --> logic +message MidsMsg { + string appId = 1; + repeated string toIds = 2; + Type type = 3; + int32 op = 4; + bytes msg = 5; +} + +// biz --> logic +message KeysMsg { + string appId = 1; + repeated string toKeys = 2; + Type type = 3; + int32 op = 4; + bytes msg = 5; +} + +// biz --> logic +message GroupMsg { + string appId = 1; + string group = 2; + Type type = 3; + int32 op = 4; + bytes msg = 5; +} + +// biz --> logic +message GroupsKey { + string appId = 1; + repeated string keys = 2; + repeated string gid = 3; +} + +// biz --> logic +message GroupsMid { + string appId = 1; + repeated string mids = 2; + repeated string gid = 3; +} + +// biz --> logic +message DelGroupsReq { + string appId = 1; + repeated string gid = 2; +} + +service Logic { + rpc Connect(ConnectReq) returns (ConnectReply); //comet + rpc Disconnect(DisconnectReq) returns (Reply); //comet + rpc Heartbeat(HeartbeatReq) returns (Reply); //comet + rpc Receive(ReceiveReq) returns (Reply); //comet + rpc PushByMids(MidsMsg) returns (Reply); //biz + rpc PushByKeys(KeysMsg) returns (Reply); //biz + rpc PushGroup(GroupMsg) returns (Reply); //biz + rpc JoinGroupsByKeys(GroupsKey) returns (Reply); //biz + rpc JoinGroupsByMids(GroupsMid) returns (Reply); //biz + rpc LeaveGroupsByKeys(GroupsKey) returns (Reply); //biz + rpc LeaveGroupsByMids(GroupsMid) returns (Reply); //biz + rpc DelGroups(DelGroupsReq) returns (Reply); //biz +} diff --git a/api/logic/grpc/logic_grpc.pb.go b/api/logic/grpc/logic_grpc.pb.go new file mode 100644 index 0000000..6ce8860 --- /dev/null +++ b/api/logic/grpc/logic_grpc.pb.go @@ -0,0 +1,497 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package grpc + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// LogicClient is the client API for Logic service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type LogicClient interface { + Connect(ctx context.Context, in *ConnectReq, opts ...grpc.CallOption) (*ConnectReply, error) + Disconnect(ctx context.Context, in *DisconnectReq, opts ...grpc.CallOption) (*Reply, error) + Heartbeat(ctx context.Context, in *HeartbeatReq, opts ...grpc.CallOption) (*Reply, error) + Receive(ctx context.Context, in *ReceiveReq, opts ...grpc.CallOption) (*Reply, error) + PushByMids(ctx context.Context, in *MidsMsg, opts ...grpc.CallOption) (*Reply, error) + PushByKeys(ctx context.Context, in *KeysMsg, opts ...grpc.CallOption) (*Reply, error) + PushGroup(ctx context.Context, in *GroupMsg, opts ...grpc.CallOption) (*Reply, error) + JoinGroupsByKeys(ctx context.Context, in *GroupsKey, opts ...grpc.CallOption) (*Reply, error) + JoinGroupsByMids(ctx context.Context, in *GroupsMid, opts ...grpc.CallOption) (*Reply, error) + LeaveGroupsByKeys(ctx context.Context, in *GroupsKey, opts ...grpc.CallOption) (*Reply, error) + LeaveGroupsByMids(ctx context.Context, in *GroupsMid, opts ...grpc.CallOption) (*Reply, error) + DelGroups(ctx context.Context, in *DelGroupsReq, opts ...grpc.CallOption) (*Reply, error) +} + +type logicClient struct { + cc grpc.ClientConnInterface +} + +func NewLogicClient(cc grpc.ClientConnInterface) LogicClient { + return &logicClient{cc} +} + +func (c *logicClient) Connect(ctx context.Context, in *ConnectReq, opts ...grpc.CallOption) (*ConnectReply, error) { + out := new(ConnectReply) + err := c.cc.Invoke(ctx, "/im.logic.Logic/Connect", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *logicClient) Disconnect(ctx context.Context, in *DisconnectReq, opts ...grpc.CallOption) (*Reply, error) { + out := new(Reply) + err := c.cc.Invoke(ctx, "/im.logic.Logic/Disconnect", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *logicClient) Heartbeat(ctx context.Context, in *HeartbeatReq, opts ...grpc.CallOption) (*Reply, error) { + out := new(Reply) + err := c.cc.Invoke(ctx, "/im.logic.Logic/Heartbeat", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *logicClient) Receive(ctx context.Context, in *ReceiveReq, opts ...grpc.CallOption) (*Reply, error) { + out := new(Reply) + err := c.cc.Invoke(ctx, "/im.logic.Logic/Receive", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *logicClient) PushByMids(ctx context.Context, in *MidsMsg, opts ...grpc.CallOption) (*Reply, error) { + out := new(Reply) + err := c.cc.Invoke(ctx, "/im.logic.Logic/PushByMids", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *logicClient) PushByKeys(ctx context.Context, in *KeysMsg, opts ...grpc.CallOption) (*Reply, error) { + out := new(Reply) + err := c.cc.Invoke(ctx, "/im.logic.Logic/PushByKeys", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *logicClient) PushGroup(ctx context.Context, in *GroupMsg, opts ...grpc.CallOption) (*Reply, error) { + out := new(Reply) + err := c.cc.Invoke(ctx, "/im.logic.Logic/PushGroup", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *logicClient) JoinGroupsByKeys(ctx context.Context, in *GroupsKey, opts ...grpc.CallOption) (*Reply, error) { + out := new(Reply) + err := c.cc.Invoke(ctx, "/im.logic.Logic/JoinGroupsByKeys", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *logicClient) JoinGroupsByMids(ctx context.Context, in *GroupsMid, opts ...grpc.CallOption) (*Reply, error) { + out := new(Reply) + err := c.cc.Invoke(ctx, "/im.logic.Logic/JoinGroupsByMids", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *logicClient) LeaveGroupsByKeys(ctx context.Context, in *GroupsKey, opts ...grpc.CallOption) (*Reply, error) { + out := new(Reply) + err := c.cc.Invoke(ctx, "/im.logic.Logic/LeaveGroupsByKeys", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *logicClient) LeaveGroupsByMids(ctx context.Context, in *GroupsMid, opts ...grpc.CallOption) (*Reply, error) { + out := new(Reply) + err := c.cc.Invoke(ctx, "/im.logic.Logic/LeaveGroupsByMids", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *logicClient) DelGroups(ctx context.Context, in *DelGroupsReq, opts ...grpc.CallOption) (*Reply, error) { + out := new(Reply) + err := c.cc.Invoke(ctx, "/im.logic.Logic/DelGroups", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// LogicServer is the server API for Logic service. +// All implementations must embed UnimplementedLogicServer +// for forward compatibility +type LogicServer interface { + Connect(context.Context, *ConnectReq) (*ConnectReply, error) + Disconnect(context.Context, *DisconnectReq) (*Reply, error) + Heartbeat(context.Context, *HeartbeatReq) (*Reply, error) + Receive(context.Context, *ReceiveReq) (*Reply, error) + PushByMids(context.Context, *MidsMsg) (*Reply, error) + PushByKeys(context.Context, *KeysMsg) (*Reply, error) + PushGroup(context.Context, *GroupMsg) (*Reply, error) + JoinGroupsByKeys(context.Context, *GroupsKey) (*Reply, error) + JoinGroupsByMids(context.Context, *GroupsMid) (*Reply, error) + LeaveGroupsByKeys(context.Context, *GroupsKey) (*Reply, error) + LeaveGroupsByMids(context.Context, *GroupsMid) (*Reply, error) + DelGroups(context.Context, *DelGroupsReq) (*Reply, error) + mustEmbedUnimplementedLogicServer() +} + +// UnimplementedLogicServer must be embedded to have forward compatible implementations. +type UnimplementedLogicServer struct { +} + +func (UnimplementedLogicServer) Connect(context.Context, *ConnectReq) (*ConnectReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method Connect not implemented") +} +func (UnimplementedLogicServer) Disconnect(context.Context, *DisconnectReq) (*Reply, error) { + return nil, status.Errorf(codes.Unimplemented, "method Disconnect not implemented") +} +func (UnimplementedLogicServer) Heartbeat(context.Context, *HeartbeatReq) (*Reply, error) { + return nil, status.Errorf(codes.Unimplemented, "method Heartbeat not implemented") +} +func (UnimplementedLogicServer) Receive(context.Context, *ReceiveReq) (*Reply, error) { + return nil, status.Errorf(codes.Unimplemented, "method Receive not implemented") +} +func (UnimplementedLogicServer) PushByMids(context.Context, *MidsMsg) (*Reply, error) { + return nil, status.Errorf(codes.Unimplemented, "method PushByMids not implemented") +} +func (UnimplementedLogicServer) PushByKeys(context.Context, *KeysMsg) (*Reply, error) { + return nil, status.Errorf(codes.Unimplemented, "method PushByKeys not implemented") +} +func (UnimplementedLogicServer) PushGroup(context.Context, *GroupMsg) (*Reply, error) { + return nil, status.Errorf(codes.Unimplemented, "method PushGroup not implemented") +} +func (UnimplementedLogicServer) JoinGroupsByKeys(context.Context, *GroupsKey) (*Reply, error) { + return nil, status.Errorf(codes.Unimplemented, "method JoinGroupsByKeys not implemented") +} +func (UnimplementedLogicServer) JoinGroupsByMids(context.Context, *GroupsMid) (*Reply, error) { + return nil, status.Errorf(codes.Unimplemented, "method JoinGroupsByMids not implemented") +} +func (UnimplementedLogicServer) LeaveGroupsByKeys(context.Context, *GroupsKey) (*Reply, error) { + return nil, status.Errorf(codes.Unimplemented, "method LeaveGroupsByKeys not implemented") +} +func (UnimplementedLogicServer) LeaveGroupsByMids(context.Context, *GroupsMid) (*Reply, error) { + return nil, status.Errorf(codes.Unimplemented, "method LeaveGroupsByMids not implemented") +} +func (UnimplementedLogicServer) DelGroups(context.Context, *DelGroupsReq) (*Reply, error) { + return nil, status.Errorf(codes.Unimplemented, "method DelGroups not implemented") +} +func (UnimplementedLogicServer) mustEmbedUnimplementedLogicServer() {} + +// UnsafeLogicServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to LogicServer will +// result in compilation errors. +type UnsafeLogicServer interface { + mustEmbedUnimplementedLogicServer() +} + +func RegisterLogicServer(s grpc.ServiceRegistrar, srv LogicServer) { + s.RegisterService(&Logic_ServiceDesc, srv) +} + +func _Logic_Connect_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ConnectReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LogicServer).Connect(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/im.logic.Logic/Connect", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LogicServer).Connect(ctx, req.(*ConnectReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Logic_Disconnect_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DisconnectReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LogicServer).Disconnect(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/im.logic.Logic/Disconnect", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LogicServer).Disconnect(ctx, req.(*DisconnectReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Logic_Heartbeat_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HeartbeatReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LogicServer).Heartbeat(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/im.logic.Logic/Heartbeat", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LogicServer).Heartbeat(ctx, req.(*HeartbeatReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Logic_Receive_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ReceiveReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LogicServer).Receive(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/im.logic.Logic/Receive", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LogicServer).Receive(ctx, req.(*ReceiveReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Logic_PushByMids_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MidsMsg) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LogicServer).PushByMids(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/im.logic.Logic/PushByMids", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LogicServer).PushByMids(ctx, req.(*MidsMsg)) + } + return interceptor(ctx, in, info, handler) +} + +func _Logic_PushByKeys_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(KeysMsg) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LogicServer).PushByKeys(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/im.logic.Logic/PushByKeys", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LogicServer).PushByKeys(ctx, req.(*KeysMsg)) + } + return interceptor(ctx, in, info, handler) +} + +func _Logic_PushGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GroupMsg) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LogicServer).PushGroup(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/im.logic.Logic/PushGroup", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LogicServer).PushGroup(ctx, req.(*GroupMsg)) + } + return interceptor(ctx, in, info, handler) +} + +func _Logic_JoinGroupsByKeys_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GroupsKey) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LogicServer).JoinGroupsByKeys(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/im.logic.Logic/JoinGroupsByKeys", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LogicServer).JoinGroupsByKeys(ctx, req.(*GroupsKey)) + } + return interceptor(ctx, in, info, handler) +} + +func _Logic_JoinGroupsByMids_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GroupsMid) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LogicServer).JoinGroupsByMids(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/im.logic.Logic/JoinGroupsByMids", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LogicServer).JoinGroupsByMids(ctx, req.(*GroupsMid)) + } + return interceptor(ctx, in, info, handler) +} + +func _Logic_LeaveGroupsByKeys_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GroupsKey) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LogicServer).LeaveGroupsByKeys(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/im.logic.Logic/LeaveGroupsByKeys", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LogicServer).LeaveGroupsByKeys(ctx, req.(*GroupsKey)) + } + return interceptor(ctx, in, info, handler) +} + +func _Logic_LeaveGroupsByMids_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GroupsMid) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LogicServer).LeaveGroupsByMids(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/im.logic.Logic/LeaveGroupsByMids", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LogicServer).LeaveGroupsByMids(ctx, req.(*GroupsMid)) + } + return interceptor(ctx, in, info, handler) +} + +func _Logic_DelGroups_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DelGroupsReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LogicServer).DelGroups(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/im.logic.Logic/DelGroups", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LogicServer).DelGroups(ctx, req.(*DelGroupsReq)) + } + return interceptor(ctx, in, info, handler) +} + +// Logic_ServiceDesc is the grpc.ServiceDesc for Logic service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Logic_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "im.logic.Logic", + HandlerType: (*LogicServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Connect", + Handler: _Logic_Connect_Handler, + }, + { + MethodName: "Disconnect", + Handler: _Logic_Disconnect_Handler, + }, + { + MethodName: "Heartbeat", + Handler: _Logic_Heartbeat_Handler, + }, + { + MethodName: "Receive", + Handler: _Logic_Receive_Handler, + }, + { + MethodName: "PushByMids", + Handler: _Logic_PushByMids_Handler, + }, + { + MethodName: "PushByKeys", + Handler: _Logic_PushByKeys_Handler, + }, + { + MethodName: "PushGroup", + Handler: _Logic_PushGroup_Handler, + }, + { + MethodName: "JoinGroupsByKeys", + Handler: _Logic_JoinGroupsByKeys_Handler, + }, + { + MethodName: "JoinGroupsByMids", + Handler: _Logic_JoinGroupsByMids_Handler, + }, + { + MethodName: "LeaveGroupsByKeys", + Handler: _Logic_LeaveGroupsByKeys_Handler, + }, + { + MethodName: "LeaveGroupsByMids", + Handler: _Logic_LeaveGroupsByMids_Handler, + }, + { + MethodName: "DelGroups", + Handler: _Logic_DelGroups_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "logic.proto", +} diff --git a/benchmarks/client_tcp/main.go b/benchmarks/client_tcp/main.go new file mode 100644 index 0000000..80b963c --- /dev/null +++ b/benchmarks/client_tcp/main.go @@ -0,0 +1,234 @@ +package main + +// Start Commond eg: ./client 1 1000 localhost:3101 +// first parameter:beginning userId +// second parameter: amount of clients +// third parameter: comet tcp-server addr + +import ( + "bufio" + "encoding/binary" + "flag" + "fmt" + "log" + "math/rand" + "net" + "os" + "runtime" + "strconv" + "sync/atomic" + "time" + + "github.com/golang/protobuf/proto" + comet "gitlab.33.cn/chat/im/api/comet/grpc" +) + +const ( + rawHeaderLen = uint16(16) + heart = 5 * time.Second +) + +type Proto struct { + PackLen int32 // package length + HeaderLen int16 // header length + Ver int16 // protocol version + Operation int32 // operation for request + Seq int32 // sequence number chosen by client + Body []byte // body +} + +var ( + countDown int64 + aliveCount int64 +) + +func main() { + runtime.GOMAXPROCS(runtime.NumCPU()) + flag.Parse() + begin, err := strconv.Atoi(os.Args[1]) + if err != nil { + panic(err) + } + num, err := strconv.Atoi(os.Args[2]) + if err != nil { + panic(err) + } + go result() + for i := begin; i < begin+num; i++ { + go client(int64(i)) + } + // signal + var exit chan bool + <-exit +} + +func result() { + var ( + lastTimes int64 + interval = int64(5) + ) + for { + nowCount := atomic.LoadInt64(&countDown) + nowAlive := atomic.LoadInt64(&aliveCount) + diff := nowCount - lastTimes + lastTimes = nowCount + fmt.Println(fmt.Sprintf("%s alive:%d down:%d down/s:%d", time.Now().Format("2006-01-02 15:04:05"), nowAlive, nowCount, diff/interval)) + time.Sleep(time.Second * time.Duration(interval)) + } +} + +func client(mid int64) { + for { + startClient(mid) + time.Sleep(time.Duration(rand.Intn(10)) * time.Second) + } +} + +func startClient(key int64) { + time.Sleep(time.Duration(rand.Intn(10)) * time.Second) + atomic.AddInt64(&aliveCount, 1) + quit := make(chan bool, 1) + defer func() { + close(quit) + atomic.AddInt64(&aliveCount, -1) + }() + // connnect to server + conn, err := net.Dial("tcp", os.Args[3]) + if err != nil { + log.Printf("net.Dial(%s) error(%v)", os.Args[3], err) + return + } + seq := int32(0) + wr := bufio.NewWriter(conn) + rd := bufio.NewReader(conn) + authMsg := &comet.AuthMsg{ + AppId: "echo", + Token: "fdasfdsaf", + } + p := new(Proto) + p.Ver = 1 + p.Operation = int32(comet.Op_Auth) + p.Seq = seq + p.Body, _ = proto.Marshal(authMsg) + + if err = tcpWriteProto(wr, p); err != nil { + log.Printf("tcpWriteProto() error(%v)", err) + return + } + if err = tcpReadProto(rd, p); err != nil { + log.Printf("tcpReadProto() error(%v)", err) + return + } + log.Printf("key:%d auth ok, proto: %v", key, p) + seq++ + // writer + go func() { + hbProto := new(Proto) + for { + // heartbeat + hbProto.Operation = int32(comet.Op_Heartbeat) + hbProto.Seq = seq + hbProto.Body = nil + if err = tcpWriteProto(wr, hbProto); err != nil { + log.Printf("key:%d tcpWriteProto() error(%v)", key, err) + return + } + log.Printf("key:%d Write heartbeat", key) + time.Sleep(heart) + seq++ + select { + case <-quit: + return + default: + } + } + }() + // reader + for { + if err = tcpReadProto(rd, p); err != nil { + log.Printf("key:%d tcpReadProto() error(%v)", key, err) + quit <- true + return + } + if p.Operation == int32(comet.Op_AuthReply) { + log.Printf("key:%d auth success", key) + } else if p.Operation == int32(comet.Op_HeartbeatReply) { + log.Printf("key:%d receive heartbeat reply", key) + if err = conn.SetReadDeadline(time.Now().Add(heart + 60*time.Second)); err != nil { + log.Printf("conn.SetReadDeadline() error(%v)", err) + quit <- true + return + } + } else { + log.Printf("key:%d op:%d msg: %s", key, p.Operation, string(p.Body)) + atomic.AddInt64(&countDown, 1) + } + } +} + +func tcpWriteProto(wr *bufio.Writer, proto *Proto) (err error) { + // write + if err = binary.Write(wr, binary.BigEndian, uint32(rawHeaderLen)+uint32(len(proto.Body))); err != nil { + return + } + if err = binary.Write(wr, binary.BigEndian, rawHeaderLen); err != nil { + return + } + if err = binary.Write(wr, binary.BigEndian, proto.Ver); err != nil { + return + } + if err = binary.Write(wr, binary.BigEndian, proto.Operation); err != nil { + return + } + if err = binary.Write(wr, binary.BigEndian, proto.Seq); err != nil { + return + } + if proto.Body != nil { + if err = binary.Write(wr, binary.BigEndian, proto.Body); err != nil { + return + } + } + err = wr.Flush() + return +} + +func tcpReadProto(rd *bufio.Reader, proto *Proto) (err error) { + var ( + packLen int32 + headerLen int16 + ) + // read + if err = binary.Read(rd, binary.BigEndian, &packLen); err != nil { + return + } + if err = binary.Read(rd, binary.BigEndian, &headerLen); err != nil { + return + } + if err = binary.Read(rd, binary.BigEndian, &proto.Ver); err != nil { + return + } + if err = binary.Read(rd, binary.BigEndian, &proto.Operation); err != nil { + return + } + if err = binary.Read(rd, binary.BigEndian, &proto.Seq); err != nil { + return + } + var ( + n, t int + bodyLen = int(packLen - int32(headerLen)) + ) + if bodyLen > 0 { + proto.Body = make([]byte, bodyLen) + for { + if t, err = rd.Read(proto.Body[n:]); err != nil { + return + } + if n += t; n == bodyLen { + break + } + } + } else { + proto.Body = nil + } + return +} diff --git a/benchmarks/client_ws/main.go b/benchmarks/client_ws/main.go new file mode 100644 index 0000000..7aa5a6b --- /dev/null +++ b/benchmarks/client_ws/main.go @@ -0,0 +1,253 @@ +package main + +// Start Commond eg: ./client 1 1000 localhost:3102 +// first parameter:beginning userId +// second parameter: amount of clients +// third parameter: comet ws-server addr + +import ( + "bufio" + "encoding/binary" + "flag" + "fmt" + "log" + "math/rand" + "os" + "runtime" + "strconv" + "sync/atomic" + "time" + + "github.com/golang/protobuf/proto" + + "github.com/gorilla/websocket" + comet "gitlab.33.cn/chat/im/api/comet/grpc" +) + +const ( + rawHeaderLen = uint16(16) + heart = 5 * time.Second +) + +type Proto struct { + PackLen int32 // package length + HeaderLen int16 // header length + Ver int16 // protocol version + Op int32 // operation for request + Seq int32 // sequence number chosen by client + Body []byte // body +} + +var ( + countDown int64 + aliveCount int64 +) + +func main() { + runtime.GOMAXPROCS(runtime.NumCPU()) + flag.Parse() + begin, err := strconv.Atoi(os.Args[1]) + if err != nil { + panic(err) + } + num, err := strconv.Atoi(os.Args[2]) + if err != nil { + panic(err) + } + go result() + for i := begin; i < begin+num; i++ { + go client(int64(i)) + } + // signal + var exit chan bool + <-exit +} + +func result() { + var ( + lastTimes int64 + interval = int64(5) + ) + for { + nowCount := atomic.LoadInt64(&countDown) + nowAlive := atomic.LoadInt64(&aliveCount) + diff := nowCount - lastTimes + lastTimes = nowCount + fmt.Println(fmt.Sprintf("%s alive:%d down:%d down/s:%d", time.Now().Format("2006-01-02 15:04:05"), nowAlive, nowCount, diff/interval)) + time.Sleep(time.Second * time.Duration(interval)) + } +} + +func client(mid int64) { + for { + startClient(mid) + time.Sleep(time.Duration(rand.Intn(10)) * time.Second) + } +} + +func startClient(key int64) { + time.Sleep(time.Duration(rand.Intn(10)) * time.Second) + atomic.AddInt64(&aliveCount, 1) + quit := make(chan bool, 1) + defer func() { + close(quit) + atomic.AddInt64(&aliveCount, -1) + }() + + // connnect to server + wsUrl := "ws://" + os.Args[3] + "/sub" + fmt.Println("wsUrl", wsUrl) + conn, _, err := websocket.DefaultDialer.Dial(wsUrl, nil) + if err != nil { + panic(err) + } + + seq := int32(0) + authMsg := &comet.AuthMsg{ + AppId: "echo", + Token: "fdasfdsaf", + } + p := new(Proto) + p.Ver = 1 + p.Op = int32(comet.Op_Auth) + p.Seq = seq + p.Body, _ = proto.Marshal(authMsg) + + //auth + if err = wsWriteProto(conn, p); err != nil { + log.Printf("wsWriteProto() error(%v)", err) + return + } + if err = wsReadProto(conn, p); err != nil { + log.Printf("tcpReadProto() error(%v)", err) + return + } + log.Printf("key:%d auth ok, proto: %v", key, p) + + seq++ + // writer + go func() { + hbProto := new(Proto) + for { + // heartbeat + hbProto.Op = int32(comet.Op_Heartbeat) + hbProto.Seq = seq + hbProto.Body = nil + if err = wsWriteProto(conn, hbProto); err != nil { + log.Printf("key:%d tcpWriteProto() error(%v)", key, err) + return + } + log.Printf("key:%d Write heartbeat", key) + time.Sleep(heart) + seq++ + select { + case <-quit: + return + default: + } + } + }() + // reader + for { + if err = wsReadProto(conn, p); err != nil { + log.Printf("key:%d tcpReadProto() error(%v)", key, err) + quit <- true + return + } + if p.Op == int32(comet.Op_AuthReply) { + log.Printf("key:%d auth success", key) + } else if p.Op == int32(comet.Op_HeartbeatReply) { + log.Printf("key:%d receive heartbeat reply", key) + if err = conn.SetReadDeadline(time.Now().Add(heart + 60*time.Second)); err != nil { + log.Printf("conn.SetReadDeadline() error(%v)", err) + quit <- true + return + } + } else { + log.Printf("key:%d op:%d msg: %s", key, p.Op, string(p.Body)) + atomic.AddInt64(&countDown, 1) + } + } +} + +func wsWriteProto(conn *websocket.Conn, proto *Proto) (err error) { + wc, err := conn.NextWriter(websocket.BinaryMessage) + if err != nil { + panic(err) + } + wr := bufio.NewWriter(wc) + + // write + if err = binary.Write(wr, binary.BigEndian, uint32(rawHeaderLen)+uint32(len(proto.Body))); err != nil { + return + } + if err = binary.Write(wr, binary.BigEndian, rawHeaderLen); err != nil { + return + } + if err = binary.Write(wr, binary.BigEndian, proto.Ver); err != nil { + return + } + if err = binary.Write(wr, binary.BigEndian, proto.Op); err != nil { + return + } + if err = binary.Write(wr, binary.BigEndian, proto.Seq); err != nil { + return + } + if proto.Body != nil { + if err = binary.Write(wr, binary.BigEndian, proto.Body); err != nil { + return + } + } + err = wr.Flush() + wc.Close() + return +} + +func wsReadProto(conn *websocket.Conn, proto *Proto) (err error) { + var ( + packLen int32 + headerLen int16 + ) + + _, rc, err := conn.NextReader() + if err != nil { + log.Printf("NextReader error(%v)", err) + return err + } + rd := bufio.NewReader(rc) + + // read + if err = binary.Read(rd, binary.BigEndian, &packLen); err != nil { + return + } + if err = binary.Read(rd, binary.BigEndian, &headerLen); err != nil { + return + } + if err = binary.Read(rd, binary.BigEndian, &proto.Ver); err != nil { + return + } + if err = binary.Read(rd, binary.BigEndian, &proto.Op); err != nil { + return + } + if err = binary.Read(rd, binary.BigEndian, &proto.Seq); err != nil { + return + } + var ( + n, t int + bodyLen = int(packLen - int32(headerLen)) + ) + if bodyLen > 0 { + proto.Body = make([]byte, bodyLen) + for { + if t, err = rd.Read(proto.Body[n:]); err != nil { + return + } + if n += t; n == bodyLen { + break + } + } + } else { + proto.Body = nil + } + return +} diff --git a/benchmarks/kafka-docker-compose.yml b/benchmarks/kafka-docker-compose.yml new file mode 100644 index 0000000..51abc51 --- /dev/null +++ b/benchmarks/kafka-docker-compose.yml @@ -0,0 +1,42 @@ +version: '2' + +services: + zoo1: + image: wurstmeister/zookeeper + restart: unless-stopped + hostname: zoo1 + ports: + - "2181:2181" + container_name: zookeeper + + # kafka version: 1.1.0 + # scala version: 2.12 + kafka1: + image: wurstmeister/kafka + ports: + - "9092:9092" + environment: + KAFKA_ADVERTISED_HOST_NAME: localhost + KAFKA_ZOOKEEPER_CONNECT: "zoo1:2181" + KAFKA_BROKER_ID: 1 + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + KAFKA_CREATE_TOPICS: "stream-in:1:1,stream-out:1:1" + depends_on: + - zoo1 + container_name: kafka + + + etcd1: + image: quay.io/coreos/etcd + container_name: etcd + command: etcd -name etcd1 -advertise-client-urls http://0.0.0.0:2379 -listen-client-urls http://0.0.0.0:2379 -listen-peer-urls http://0.0.0.0:2380 -initial-cluster-token tkn -initial-cluster-state new + ports: + - "2379:2379" + - "2380:2380" + + redis1: + image: redis + container_name: redis + command: redis-server + ports: + - "6379:6379" \ No newline at end of file diff --git a/comet/CHANGELOG.md b/comet/CHANGELOG.md new file mode 100644 index 0000000..7caa186 --- /dev/null +++ b/comet/CHANGELOG.md @@ -0,0 +1,29 @@ +版本号`major.minor.patch`具体规则如下: +- major:主版本号,如有重大版本重构则该字段递增,通常各主版本间接口不兼容。 +- minor:次版本号,各次版本号间接口保持兼容,如有接口新增或优化则该字段递增。 +- patch:补丁号,如有功能改善或缺陷修复则该字段递增。 + +## version 3.0.2 @2021.10.28 + +优化 log 模块 + +配置文件更新 +新增 +```toml +[log] + Level="debug" + Mode="console" + Path="" + Display="json" +``` + + +## example x.x.x @yy.mm.dd + +**Feature** + +**Bug Fixes** + +**Improvement** + +**Breaking Change** diff --git a/comet/bucket.go b/comet/bucket.go new file mode 100644 index 0000000..dfc621d --- /dev/null +++ b/comet/bucket.go @@ -0,0 +1,159 @@ +package comet + +import ( + "sync" + "sync/atomic" + + "gitlab.33.cn/chat/im/api/comet/grpc" + "gitlab.33.cn/chat/im/comet/conf" +) + +// Bucket is a channel holder. +type Bucket struct { + c *conf.Bucket + cLock sync.RWMutex // protect the channels for chs + chs map[string]*Channel // map sub key to a channel + //group + groups map[string]*Group + routines []chan *grpc.BroadcastGroupReq + routinesNum uint64 +} + +// NewBucket new a bucket struct. store the key with im channel. +func NewBucket(c *conf.Bucket) (b *Bucket) { + b = new(Bucket) + b.chs = make(map[string]*Channel, c.Channel) + b.c = c + b.groups = make(map[string]*Group, c.Groups) + b.routines = make([]chan *grpc.BroadcastGroupReq, c.RoutineAmount) + for i := uint64(0); i < c.RoutineAmount; i++ { + c := make(chan *grpc.BroadcastGroupReq, c.RoutineSize) + b.routines[i] = c + go b.groupProc(c) + } + return +} + +// ChannelCount channel count in the bucket +func (b *Bucket) ChannelCount() int { + return len(b.chs) +} + +// Put put a channel according with sub key. +func (b *Bucket) Put(ch *Channel) (err error) { + b.cLock.Lock() + // close old channel + if dch := b.chs[ch.Key]; dch != nil { + dch.Close() + } + b.chs[ch.Key] = ch + b.cLock.Unlock() + return +} + +// Del delete the channel by sub key. +func (b *Bucket) Del(dch *Channel) { + var ( + ok bool + ch *Channel + ) + b.cLock.Lock() + if ch, ok = b.chs[dch.Key]; ok { + if ch == dch { + //修改内容:获取channel下所有添加的群,并逐个在群聊中删除该channel;修改人:dld;修改时间:2021年5月8日16:36:00 c4f618a9-1c37-3459-3861-a24f26bb2d85 + for id := range ch.Groups() { + if g := b.groups[id]; g != nil { + g.Del(ch) + } + } + //结束:c4f618a9-1c37-3459-3861-a24f26bb2d85 + delete(b.chs, ch.Key) + } + } + b.cLock.Unlock() +} + +// Channel get a channel by sub key. +func (b *Bucket) Channel(key string) (ch *Channel) { + b.cLock.RLock() + ch = b.chs[key] + b.cLock.RUnlock() + return +} + +// Broadcast push msgs to all channels in the bucket. +func (b *Bucket) Broadcast(p *grpc.Proto, op int32) { + var ch *Channel + b.cLock.RLock() + for _, ch = range b.chs { + _, _ = ch.Push(p) + } + b.cLock.RUnlock() +} + +// group +// GroupCount room count in the bucket +func (b *Bucket) GroupCount() int { + return len(b.groups) +} + +// GroupsCount get all group id where online number > 0. +func (b *Bucket) GroupsCount() (res map[string]int32) { + var ( + groupID string + group *Group + ) + b.cLock.RLock() + res = make(map[string]int32) + for groupID, group = range b.groups { + if group.Online > 0 { + res[groupID] = group.Online + } + } + b.cLock.RUnlock() + return +} + +// Put put a group according with sub key. +func (b *Bucket) PutGroup(gid string) (group *Group, err error) { + var ok bool + b.cLock.Lock() + if group, ok = b.groups[gid]; !ok { + group = NewGroup(gid) + b.groups[gid] = group + } + b.cLock.Unlock() + return +} + +// Group get a group by group id. +func (b *Bucket) Group(gid string) (group *Group) { + b.cLock.RLock() + group = b.groups[gid] + b.cLock.RUnlock() + return +} + +// DelGroup delete a room by group id. +func (b *Bucket) DelGroup(group *Group) { + b.cLock.Lock() + delete(b.groups, group.ID) + b.cLock.Unlock() + group.Close() +} + +// BroadcastGroup broadcast a message to specified group +func (b *Bucket) BroadcastGroup(arg *grpc.BroadcastGroupReq) { + num := atomic.AddUint64(&b.routinesNum, 1) % b.c.RoutineAmount + b.routines[num] <- arg +} + +// group proc +func (b *Bucket) groupProc(c chan *grpc.BroadcastGroupReq) { + for { + arg := <-c + if group := b.Group(arg.GroupID); group != nil { + group.Push(arg.Proto) + } + } +} diff --git a/comet/channel.go b/comet/channel.go new file mode 100644 index 0000000..da760a4 --- /dev/null +++ b/comet/channel.go @@ -0,0 +1,102 @@ +package comet + +import ( + "errors" + "sync" + "sync/atomic" + + "github.com/Terry-Mao/goim/pkg/bufio" + "github.com/golang/protobuf/proto" + "gitlab.33.cn/chat/im/api/comet/grpc" +) + +// Channel used by message pusher send msg to write goroutine. +type Channel struct { + CliProto Ring + signal chan *grpc.Proto + Writer bufio.Writer + Reader bufio.Reader + + Seq int32 + Key string + IP string + Port string + + nodes map[string]*Node + mutex sync.RWMutex +} + +// NewChannel new a channel. +func NewChannel(cli, svr int) *Channel { + c := new(Channel) + c.CliProto.Init(cli) + c.signal = make(chan *grpc.Proto, svr) + c.nodes = make(map[string]*Node) + return c +} + +// Push server push message. +func (c *Channel) seqInc() int32 { + return atomic.AddInt32(&c.Seq, 1) +} + +// Push server push message. +func (c *Channel) push(p *grpc.Proto) (err error) { + select { + case c.signal <- p: + default: + } + return +} + +// Push server push message. +func (c *Channel) Push(p *grpc.Proto) (seq int32, err error) { + if p, ok := proto.Clone(p).(*grpc.Proto); ok { + if p.Op == int32(grpc.Op_ReceiveMsg) { + p.Seq = c.seqInc() + } + seq = p.Seq + return seq, c.push(p) + } else { + return 0, errors.New("protocol type gRPC proto failed") + } +} + +// Ready check the channel ready or close? +func (c *Channel) Ready() *grpc.Proto { + return <-c.signal +} + +// Signal send signal to the channel, protocol ready. +func (c *Channel) Signal() { + c.signal <- grpc.ProtoReady +} + +// Close close the channel. +func (c *Channel) Close() { + c.signal <- grpc.ProtoFinish +} + +// Close close the channel. +func (c Channel) Groups() map[string]*Node { + return c.nodes +} + +// +func (c *Channel) DelNode(id string) { + c.mutex.Lock() + delete(c.nodes, id) + c.mutex.Unlock() +} + +func (c *Channel) SetNode(id string, node *Node) { + c.mutex.Lock() + c.nodes[id] = node + c.mutex.Unlock() +} + +func (c *Channel) GetNode(id string) *Node { + c.mutex.Lock() + defer c.mutex.Unlock() + return c.nodes[id] +} diff --git a/comet/cmd/main.go b/comet/cmd/main.go new file mode 100644 index 0000000..8ec7d56 --- /dev/null +++ b/comet/cmd/main.go @@ -0,0 +1,135 @@ +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "math/rand" + "net" + _ "net/http/pprof" + "os" + "os/signal" + "runtime" + "syscall" + "time" + + "github.com/Terry-Mao/goim/pkg/ip" + "github.com/opentracing/opentracing-go" + "github.com/rs/zerolog/log" + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/config" + xlog "gitlab.33.cn/chat/im-pkg/log" + "gitlab.33.cn/chat/im-pkg/trace" + "gitlab.33.cn/chat/im/comet" + "gitlab.33.cn/chat/im/comet/conf" + "gitlab.33.cn/chat/im/comet/grpc" + "gitlab.33.cn/chat/im/comet/http" + "gitlab.33.cn/chat/im/naming" +) + +const ( + srvName = "comet" +) + +var ( + // projectVersion 项目版本 + projectVersion = "3.0.3" + // goVersion go版本 + goVersion = "" + // gitCommit git提交commit id + gitCommit = "" + // buildTime 编译时间 + buildTime = "" + + isShowVersion = flag.Bool("version", false, "show project version") +) + +// showVersion 显示项目版本信息 +func showVersion(isShow bool) { + if isShow { + fmt.Printf("Project: %s\n", srvName) + fmt.Printf(" Version: %s\n", projectVersion) + fmt.Printf(" Go Version: %s\n", goVersion) + fmt.Printf(" Git Commit: %s\n", gitCommit) + fmt.Printf(" Build Time: %s\n", buildTime) + os.Exit(0) + } +} + +func main() { + flag.Parse() + showVersion(*isShowVersion) + + if err := conf.Init(); err != nil { + panic(err) + } + rand.Seed(time.Now().UTC().UnixNano()) + runtime.GOMAXPROCS(runtime.NumCPU()) + + //log init + var err error + log.Logger, err = xlog.Init(conf.Conf.Log) + if err != nil { + panic(err) + } + log.Logger.With().Str("service", srvName) + + byte, _ := json.Marshal(conf.Conf) + log.Info().Str("config", string(byte)).Send() + + // trace init + tracer, tracerCloser := trace.Init(srvName, conf.Conf.Trace, config.Logger(jaeger.NullLogger)) + //不然后续不会有Jaeger实例 + opentracing.SetGlobalTracer(tracer) + + srv := comet.New(conf.Conf) + rpcSrv := grpc.New(conf.Conf.RPCServer, srv) + httpSrv := http.Start(":8000", srv) + + if err := comet.InitWebsocket(srv, conf.Conf.Websocket.Bind, runtime.NumCPU()); err != nil { + panic(err) + } + if err := comet.InitTCP(srv, conf.Conf.TCP.Bind, runtime.NumCPU()); err != nil { + panic(err) + } + + // register comet + _, port, _ := net.SplitHostPort(conf.Conf.RPCServer.Addr) + addr := fmt.Sprintf("%s:%s", ip.InternalIP(), port) + if err := naming.Register(conf.Conf.Reg.RegAddrs, conf.Conf.Reg.SrvName, addr, conf.Conf.Reg.Schema, 15); err != nil { + panic(err) + } + fmt.Println("register ok") + + // signal + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) + for { + s := <-c + log.Info().Str("signal", s.String()).Send() + switch s { + case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT: + if err := naming.UnRegister(conf.Conf.Reg.SrvName, addr, conf.Conf.Reg.Schema); err != nil { + log.Error().Err(err).Msg("naming.UnRegister") + } + rpcSrv.GracefulStop() + srv.Close() + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := httpSrv.Shutdown(ctx); err != nil { + log.Fatal().Err(err).Msg("http server shutdown") + } + if err := tracerCloser.Close(); err != nil { + log.Error().Err(err).Msg("tracer close failed") + } + log.Info().Msg("comet exit") + xlog.Close() + //log.Flush() + return + case syscall.SIGHUP: + default: + return + } + } +} diff --git a/comet/comet.go b/comet/comet.go new file mode 100644 index 0000000..1efb408 --- /dev/null +++ b/comet/comet.go @@ -0,0 +1,126 @@ +package comet + +import ( + "context" + "fmt" + "math/rand" + "net" + "time" + + "github.com/Terry-Mao/goim/pkg/ip" + "github.com/zhenjl/cityhash" + "gitlab.33.cn/chat/im-pkg/trace" + comet "gitlab.33.cn/chat/im/api/comet/grpc" + logic "gitlab.33.cn/chat/im/api/logic/grpc" + "gitlab.33.cn/chat/im/comet/conf" + "gitlab.33.cn/chat/im/common" + "gitlab.33.cn/chat/im/naming" + "google.golang.org/grpc" + "google.golang.org/grpc/resolver" +) + +// Comet is comet server. +type Comet struct { + c *conf.Config + round *Round + buckets []*Bucket // subkey bucket + bucketIdx uint32 + + serverID string + logicRPCClient logic.LogicClient +} + +// NewServer returns a new Server. +func New(c *conf.Config) *Comet { + s := &Comet{ + c: c, + round: NewRound(c), + logicRPCClient: newLogicClient(c), + } + // init bucket + s.buckets = make([]*Bucket, c.Bucket.Size) + s.bucketIdx = uint32(c.Bucket.Size) + for i := 0; i < c.Bucket.Size; i++ { + s.buckets[i] = NewBucket(c.Bucket) + } + addr := ip.InternalIP() + _, port, _ := net.SplitHostPort(c.RPCServer.Addr) + s.serverID = "grpc://" + addr + ":" + port + return s +} + +func newLogicClient(c *conf.Config) logic.LogicClient { + rb := naming.NewResolver(c.Reg.RegAddrs, c.LogicRPCClient.Schema) + resolver.Register(rb) + + addr := fmt.Sprintf("%s:///%s", c.LogicRPCClient.Schema, c.LogicRPCClient.SrvName) // "schema://[authority]/service" + fmt.Println("rpc client call addr:", addr) + + conn, err := common.NewGRPCConn(addr, time.Duration(c.LogicRPCClient.Dial), grpc.WithUnaryInterceptor(trace.OpentracingClientInterceptor)) + if err != nil { + panic(err) + } + return logic.NewLogicClient(conn) +} + +// Buckets return all buckets. +func (s *Comet) Buckets() []*Bucket { + return s.buckets +} + +// Bucket get the bucket by subkey. +func (s *Comet) Bucket(subKey string) *Bucket { + idx := cityhash.CityHash32([]byte(subKey), uint32(len(subKey))) % s.bucketIdx + return s.buckets[idx] +} + +// RandServerHearbeat rand server heartbeat. +func (s *Comet) RandServerHearbeat() time.Duration { + return time.Duration(s.c.Protocol.MinHeartbeat) + time.Duration(rand.Int63n(int64(s.c.Protocol.MaxHeartbeat-s.c.Protocol.MinHeartbeat))) +} + +// Close close the server. +func (s *Comet) Close() (err error) { + return +} + +// Connect connected a connection. +func (s *Comet) Connect(c context.Context, p *comet.Proto) (key string, hb time.Duration, err error) { + var ( + req logic.ConnectReq + reply *logic.ConnectReply + ) + + req.Server = s.serverID + req.Proto = p + reply, err = s.logicRPCClient.Connect(c, &req) + if err != nil { + return + } + + return reply.Key, time.Duration(reply.Heartbeat), nil +} + +// Disconnect disconnected a connection. +func (s *Comet) Disconnect(c context.Context, key string) (err error) { + _, err = s.logicRPCClient.Disconnect(context.Background(), &logic.DisconnectReq{ + Server: s.serverID, + Key: key, + }) + return +} + +// Heartbeat heartbeat a connection session. +func (s *Comet) Heartbeat(ctx context.Context, key string) (err error) { + _, err = s.logicRPCClient.Heartbeat(ctx, &logic.HeartbeatReq{ + Server: s.serverID, + Key: key, + }) + return +} + +// Receive receive a message. +func (s *Comet) Receive(ctx context.Context, key string, p *comet.Proto) (err error) { + _, err = s.logicRPCClient.Receive(ctx, &logic.ReceiveReq{Key: key, Proto: p}) + return +} diff --git a/comet/conf/comet.toml b/comet/conf/comet.toml new file mode 100644 index 0000000..0c7de68 --- /dev/null +++ b/comet/conf/comet.toml @@ -0,0 +1,70 @@ +env="debug" + +[log] + Level="debug" + Mode="console" + Path="" + Display="json" + +[Trace] + ServiceName="" + Gen128Bit=true +[Trace.Sampler] + Type="const" + Param=1.0 +[Trace.Reporter] + LogSpans=true + LocalAgentHostPort="172.16.101.130:6831" + +[reg] + schema = "im" + srvName = "comet" + regAddrs = "127.0.0.1:2379" + +[logicRPCClient] + schema = "im" + srvName = "logic" + dial = "1s" + timeout = "1s" + +[RPCServer] + Network = "tcp" + Addr = ":3109" + Timeout = "1s" + KeepAliveMaxConnectionIdle = "60s" + KeepAliveMaxConnectionAge = "2h" + KeepAliveMaxMaxConnectionAgeGrace = "20s" + KeepAliveTime = "60s" + KeepAliveTimeout = "20s" + +[tcp] + bind = [":3101"] + sndbuf = 4096 + rcvbuf = 4096 + keepalive = false + reader = 32 + readBuf = 1024 + readBufSize = 8192 + writer = 32 + writeBuf = 1024 + writeBufSize = 8192 + +[websocket] + bind = [":3102"] + tlsOpen = false + tlsBind = [":3103"] + certFile = "../../cert.pem" + privateFile = "../../private.pem" + +[protocol] + timer = 32 + timerSize = 2048 + svrProto = 10 + cliProto = 5 + handshakeTimeout = "8s" + minHeartbeat = "5s" + maxHeartbeat = "10s" + +[bucket] + size = 32 + channel = 1024 \ No newline at end of file diff --git a/comet/conf/conf.go b/comet/conf/conf.go new file mode 100644 index 0000000..5d184e7 --- /dev/null +++ b/comet/conf/conf.go @@ -0,0 +1,209 @@ +package conf + +import ( + "flag" + "github.com/uber/jaeger-client-go" + "os" + "time" + + "github.com/BurntSushi/toml" + xtime "github.com/Terry-Mao/goim/pkg/time" + traceConfig "github.com/uber/jaeger-client-go/config" + xlog "gitlab.33.cn/chat/im-pkg/log" +) + +const ( + DebugMode = "debug" + ReleaseMode = "release" + TestMode = "test" +) + +var ( + confPath string + regAddress string + + // Conf config + Conf *Config +) + +func init() { + var ( + defAddress = os.Getenv("REGADDRS") + ) + flag.StringVar(&confPath, "conf", "comet.toml", "default config path.") + flag.StringVar(®Address, "reg", defAddress, "etcd register addrs. eg:127.0.0.1:2379") +} + +// Init init config. +func Init() (err error) { + Conf = Default() + _, err = toml.DecodeFile(confPath, &Conf) + return +} + +// Default new a config with specified defualt value. +func Default() *Config { + return &Config{ + Env: "", + Log: xlog.Config{ + Level: "debug", + Mode: "console", + Path: "", + Display: "console", + }, + Trace: traceConfig.Configuration{ + ServiceName: "answer", + Gen128Bit: true, + Sampler: &traceConfig.SamplerConfig{ + Type: jaeger.SamplerTypeConst, + Param: 1, + }, + Reporter: &traceConfig.ReporterConfig{ + LogSpans: true, + LocalAgentHostPort: "127.0.0.1:6831", + }, + }, + Reg: &Reg{ + Schema: "im", + SrvName: "comet", + RegAddrs: regAddress, + }, + LogicRPCClient: &RPCClient{ + Schema: "im", + SrvName: "logic", + Dial: xtime.Duration(time.Second), + Timeout: xtime.Duration(time.Second), + }, + RPCServer: &RPCServer{ + Network: "tcp", + Addr: ":3109", + Timeout: xtime.Duration(time.Second), + IdleTimeout: xtime.Duration(time.Second * 60), + MaxLifeTime: xtime.Duration(time.Hour * 2), + ForceCloseWait: xtime.Duration(time.Second * 20), + KeepAliveInterval: xtime.Duration(time.Second * 60), + KeepAliveTimeout: xtime.Duration(time.Second * 20), + }, + TCP: &TCP{ + Bind: []string{":3101"}, + Sndbuf: 4096, + Rcvbuf: 4096, + KeepAlive: false, + Reader: 32, + ReadBuf: 1024, + ReadBufSize: 8192, + Writer: 32, + WriteBuf: 1024, + WriteBufSize: 8192, + }, + Websocket: &Websocket{ + Bind: []string{":3102"}, + }, + Protocol: &Protocol{ + Timer: 32, + TimerSize: 2048, + Task: 32, + TaskSize: 2048, + CliProto: 5, + SvrProto: 10, + HandshakeTimeout: xtime.Duration(time.Second * 5), + TaskDuration: xtime.Duration(time.Second * 5), + MinHeartbeat: xtime.Duration(time.Minute * 10), + MaxHeartbeat: xtime.Duration(time.Minute * 30), + }, + Bucket: &Bucket{ + Size: 32, + Channel: 1024, + Groups: 1024, + RoutineAmount: 32, + RoutineSize: 1024, + }, + } +} + +// Config is comet config. +type Config struct { + Env string + Log xlog.Config + Trace traceConfig.Configuration + Reg *Reg + TCP *TCP + Websocket *Websocket + Protocol *Protocol + Bucket *Bucket + LogicRPCClient *RPCClient + RPCServer *RPCServer +} + +// Reg is service register/discovery config +type Reg struct { + Schema string + SrvName string // call + RegAddrs string // etcd addrs, seperate by ',' +} + +// RPCClient is RPC client config. +type RPCClient struct { + Schema string + SrvName string // call + Dial xtime.Duration + Timeout xtime.Duration +} + +// RPCServer is RPC server config. +type RPCServer struct { + Network string + Addr string + Timeout xtime.Duration + IdleTimeout xtime.Duration + MaxLifeTime xtime.Duration + ForceCloseWait xtime.Duration + KeepAliveInterval xtime.Duration + KeepAliveTimeout xtime.Duration +} + +// TCP is tcp config. +type TCP struct { + Bind []string + Sndbuf int + Rcvbuf int + KeepAlive bool + Reader int + ReadBuf int + ReadBufSize int + Writer int + WriteBuf int + WriteBufSize int +} + +// Websocket is websocket config. +type Websocket struct { + Bind []string + TLSOpen bool + TLSBind []string + CertFile string + PrivateFile string +} + +// Protocol is protocol config. +type Protocol struct { + Timer int + TimerSize int + Task int + TaskSize int + SvrProto int + CliProto int + HandshakeTimeout xtime.Duration + MinHeartbeat xtime.Duration + MaxHeartbeat xtime.Duration + TaskDuration xtime.Duration +} + +// Bucket is bucket config. +type Bucket struct { + Size int + Channel int + Groups int + RoutineAmount uint64 + RoutineSize int +} diff --git a/comet/errors/errors.go b/comet/errors/errors.go new file mode 100644 index 0000000..c4978f0 --- /dev/null +++ b/comet/errors/errors.go @@ -0,0 +1,34 @@ +package errors + +import ( + "errors" +) + +// . +var ( + // server + ErrHandshake = errors.New("handshake failed") + ErrOperation = errors.New("request operation not valid") + // ring + ErrRingEmpty = errors.New("ring buffer empty") + ErrRingFull = errors.New("ring buffer full") + // timer + ErrTimerFull = errors.New("timer full") + ErrTimerEmpty = errors.New("timer empty") + ErrTimerNoItem = errors.New("timer item not exist") + // channel + ErrUnconnected = errors.New("client unconnected error") + ErrJoinGroupArg = errors.New("rpc joingroup arg error") + ErrPushMsgArg = errors.New("rpc pushmsg arg error") + ErrPushMsgsArg = errors.New("rpc pushmsgs arg error") + ErrMPushMsgArg = errors.New("rpc mpushmsg arg error") + ErrMPushMsgsArg = errors.New("rpc mpushmsgs arg error") + // bucket + ErrBroadCastArg = errors.New("rpc broadcast arg error") + ErrBroadCastRoomArg = errors.New("rpc broadcast room arg error") + + // group + ErrGroupDroped = errors.New("group droped") + // rpc + ErrLogic = errors.New("logic rpc is not available") +) diff --git a/comet/group.go b/comet/group.go new file mode 100644 index 0000000..b15073a --- /dev/null +++ b/comet/group.go @@ -0,0 +1,119 @@ +package comet + +import ( + "fmt" + "sync" + + "gitlab.33.cn/chat/im/api/comet/grpc" +) + +// Group is a group and store channel group info. +type Group struct { + ID string + rLock sync.RWMutex + next *Node + drop bool + Online int32 // dirty read is ok + AllOnline int32 +} + +// NewGroup new a group struct, store channel group info. +func NewGroup(id string) (r *Group) { + r = new(Group) + r.ID = id + r.drop = false + r.next = nil + r.Online = 0 + return +} + +// Put put channel into the group. +func (r *Group) Put(ch *Channel) (err error) { + r.rLock.Lock() + if !r.drop && ch.GetNode(r.ID) == nil { + node := &Node{ + Current: ch, + Next: nil, + Prev: nil, + } + ch.SetNode(r.ID, node) + if r.next != nil { + r.next.Prev = node + } + node.Next = r.next + node.Prev = nil + r.next = node // insert to header + r.Online++ + } /* else { //del: 2021年7月21日16:32:54 dld + err = errors.ErrGroupDroped + }*/ + r.rLock.Unlock() + return +} + +// Del delete channel from the group. +func (r *Group) Del(ch *Channel) bool { + r.rLock.Lock() + if node := ch.GetNode(r.ID); node != nil { + if node.Next != nil { + // if not footer + node.Next.Prev = node.Prev + } + if node.Prev != nil { + // if not header + node.Prev.Next = node.Next + } else { + r.next = node.Next + } + r.Online-- + //r.drop = (r.Online == 0) + ch.DelNode(r.ID) //2021年6月10日 删除对应node,防止再次put的时候报ErrGroupDroped错误;dld + } + r.rLock.Unlock() + return r.drop +} + +// Push push msg to the group, if chan full discard it. +func (r *Group) Push(p *grpc.Proto) { + r.rLock.RLock() + for node := r.next; node != nil; node = node.Next { + if node.Current != nil { + _, _ = node.Current.Push(p) + } + } + r.rLock.RUnlock() +} + +// group members Key,IP +func (r *Group) Members() ([]string, []string) { + r.rLock.RLock() + members := make([]string, 0) + mIp := make([]string, 0) + for node := r.next; node != nil; node = node.Next { + if node.Current != nil { + members = append(members, node.Current.Key) + mIp = append(mIp, fmt.Sprintf("%v:%v", node.Current.IP, node.Current.Port)) + } + } + r.rLock.RUnlock() + return members, mIp +} + +// Close close the group. +func (r *Group) Close() { + r.rLock.Lock() + for node := r.next; node != nil; node = node.Next { + if ch := node.Current; ch != nil { + ch.DelNode(r.ID) + } + } + r.rLock.Unlock() +} + +// OnlineNum the group all online. +func (r *Group) OnlineNum() int32 { + if r.AllOnline > 0 { + return r.AllOnline + } + return r.Online +} diff --git a/comet/group_test.go b/comet/group_test.go new file mode 100644 index 0000000..82cdf7f --- /dev/null +++ b/comet/group_test.go @@ -0,0 +1,121 @@ +package comet + +import ( + "strconv" + "sync/atomic" + "testing" + "time" +) + +func TestGroup_Put(t *testing.T) { + //one channel + var gid int32 = 0 + ch := NewChannel(10, 10) + //one groups + { + g := NewGroup(strconv.Itoa(int(atomic.AddInt32(&gid, 1)))) + for i := 0; i < 10; i++ { + go func() { + for { + err := g.Put(ch) + if err != nil { + t.Error(err) + } + } + }() + } + } + + //many groups + { + for i := 0; i < 10; i++ { + g := NewGroup(strconv.Itoa(int(atomic.AddInt32(&gid, 1)))) + go func() { + for { + err := g.Put(ch) + if err != nil { + t.Error(err) + } + } + }() + } + } + + time.Sleep(10 * time.Second) +} + +func TestGroup_Del(t *testing.T) { + //one channel + var gid int32 = 0 + //var lc sync.RWMutex + var groups = make(map[int32]*Group) + ch := NewChannel(10, 10) + //init + for i := 0; i < 10; i++ { + g := NewGroup(strconv.Itoa(int(atomic.AddInt32(&gid, 1)))) + groups[gid] = g + } + gid = 0 + + go func() { + for _, g := range groups { + err := g.Put(ch) + if err != nil { + t.Error(err) + } + } + }() + + go func() { + for _, g := range groups { + g.Del(ch) + } + }() + time.Sleep(10 * time.Second) +} + +func TestGroup_Del2(t *testing.T) { + //one channel + var gid int32 = 0 + //var lc sync.RWMutex + var groups = make(map[int32]*Group) + ch := NewChannel(10, 10) + //init + for i := 0; i < 10; i++ { + g := NewGroup(strconv.Itoa(int(atomic.AddInt32(&gid, 1)))) + groups[gid] = g + } + gid = 0 + + go func() { + for _, g := range groups { + err := g.Put(ch) + if err != nil { + t.Error(err) + } + } + }() + + go func() { + for _, g := range groups { + g.Del(ch) + } + }() + time.Sleep(10 * time.Second) +} + +func convertAddress(addr string) string { + if len(addr) == 0 { + return addr + } + switch addr[0] { + case 'g': + return addr[7:] + default: + return addr + } +} + +func Test_convertAddress(t *testing.T) { + t.Log(convertAddress("grpc://172.16.101.107:3109")) +} diff --git a/comet/grpc/server.go b/comet/grpc/server.go new file mode 100644 index 0000000..ce61efa --- /dev/null +++ b/comet/grpc/server.go @@ -0,0 +1,152 @@ +package grpc + +import ( + "context" + "gitlab.33.cn/chat/im-pkg/trace" + "github.com/rs/zerolog/log" + "net" + "time" + + pb "gitlab.33.cn/chat/im/api/comet/grpc" + "gitlab.33.cn/chat/im/comet" + "gitlab.33.cn/chat/im/comet/conf" + "gitlab.33.cn/chat/im/comet/errors" + + "google.golang.org/grpc" + "google.golang.org/grpc/keepalive" +) + +// New comet grpc server. +func New(c *conf.RPCServer, s *comet.Comet) *grpc.Server { + keepParams := grpc.KeepaliveParams(keepalive.ServerParameters{ + MaxConnectionIdle: time.Duration(c.IdleTimeout), + MaxConnectionAgeGrace: time.Duration(c.ForceCloseWait), + Time: time.Duration(c.KeepAliveInterval), + Timeout: time.Duration(c.KeepAliveTimeout), + MaxConnectionAge: time.Duration(c.MaxLifeTime), + }) + connectionTimeout := grpc.ConnectionTimeout(time.Duration(c.Timeout)) + srv := grpc.NewServer(keepParams, connectionTimeout, + grpc.ChainUnaryInterceptor( + trace.OpentracingServerInterceptor, + )) + pb.RegisterCometServer(srv, &server{srv: s}) + lis, err := net.Listen(c.Network, c.Addr) + if err != nil { + panic(err) + } + go func() { + if err := srv.Serve(lis); err != nil { + panic(err) + } + }() + return srv +} + +type server struct { + pb.UnimplementedCometServer + srv *comet.Comet +} + +// PushMsg push a message to specified sub keys. +func (s *server) PushMsg(ctx context.Context, req *pb.PushMsgReq) (reply *pb.PushMsgReply, err error) { + if len(req.Keys) == 0 || req.Proto == nil { + return nil, errors.ErrPushMsgArg + } + index := make(map[string]int32) + var seq int32 + for _, key := range req.Keys { + if channel := s.srv.Bucket(key).Channel(key); channel != nil { + if seq, err = channel.Push(req.Proto); err != nil { + return + } + index[key] = seq + } + } + return &pb.PushMsgReply{Index: index}, nil +} + +// Broadcast broadcast msg to all user. +func (s *server) Broadcast(ctx context.Context, req *pb.BroadcastReq) (*pb.BroadcastReply, error) { + if req.Proto == nil { + return nil, errors.ErrBroadCastArg + } + // TODO use broadcast queue + go func() { + for _, bucket := range s.srv.Buckets() { + bucket.Broadcast(req.GetProto(), req.ProtoOp) + } + }() + return &pb.BroadcastReply{}, nil +} + +func (s *server) BroadcastGroup(ctx context.Context, req *pb.BroadcastGroupReq) (*pb.BroadcastGroupReply, error) { + if req.Proto == nil || req.GroupID == "" { + return nil, errors.ErrBroadCastArg + } + for _, bucket := range s.srv.Buckets() { + bucket.BroadcastGroup(req) + } + return &pb.BroadcastGroupReply{}, nil +} + +func (s *server) JoinGroups(ctx context.Context, req *pb.JoinGroupsReq) (*pb.JoinGroupsReply, error) { + if len(req.Keys) == 0 || len(req.Gid) == 0 { + return nil, errors.ErrJoinGroupArg + } + for _, key := range req.Keys { + var channel *comet.Channel + bucket := s.srv.Bucket(key) + if channel = bucket.Channel(key); channel == nil { + log.Error().Str("key", key).Msg("JoinGroups get channel err") + continue + //return &pb.JoinGroupsReply{}, errors.ErrUnconnected todo 2021_12_08_14_36: + } + for _, gid := range req.Gid { + var group *comet.Group + if group = bucket.Group(gid); group == nil { + group, _ = bucket.PutGroup(gid) + } + err := group.Put(channel) + if err != nil { + log.Error().Err(err).Str("key", key).Str("gid", gid). + Int32("channel.Seq", channel.Seq).Str("channel.Key", channel.Key).Str("channel.Ip", channel.IP).Str("channel.Port", channel.Port). + Msg("JoinGroups get channel err") + continue + //return &pb.JoinGroupsReply{}, err todo 2021_12_08_14_36: + } + } + } + return &pb.JoinGroupsReply{}, nil +} + +func (s *server) LeaveGroups(ctx context.Context, req *pb.LeaveGroupsReq) (*pb.LeaveGroupsReply, error) { + if len(req.Keys) == 0 || len(req.Gid) == 0 { + return nil, errors.ErrPushMsgArg + } + for _, key := range req.Keys { + var channel *comet.Channel + bucket := s.srv.Bucket(key) + if channel = bucket.Channel(key); channel == nil { + continue + //return &pb.LeaveGroupsReply{}, errors.ErrUnconnected todo 2021_12_08_14_36: + } + for _, gid := range req.Gid { + if group := bucket.Group(gid); group != nil { + group.Del(channel) + } + } + } + return &pb.LeaveGroupsReply{}, nil +} + +func (s *server) DelGroups(ctx context.Context, req *pb.DelGroupsReq) (*pb.DelGroupsReply, error) { + for _, gid := range req.Gid { + for _, bucket := range s.srv.Buckets() { + if g := bucket.Group(gid); g != nil { + bucket.DelGroup(g) + } + } + } + return &pb.DelGroupsReply{}, nil +} diff --git a/comet/http/result.go b/comet/http/result.go new file mode 100644 index 0000000..ab03abe --- /dev/null +++ b/comet/http/result.go @@ -0,0 +1,36 @@ +package http + +import "github.com/gin-gonic/gin" + +const ( + // OK ok + OK = 0 + // RequestErr request error + RequestErr = -400 + // ServerErr server error + ServerErr = -500 + + contextErrCode = "context/err/code" +) + +type resp struct { + Code int `json:"code"` + Message string `json:"message"` + Data interface{} `json:"data,omitempty"` +} + +func errors(c *gin.Context, code int, msg string) { + c.Set(contextErrCode, code) + c.JSON(200, resp{ + Code: code, + Message: msg, + }) +} + +func result(c *gin.Context, data interface{}, code int) { + c.Set(contextErrCode, code) + c.JSON(200, resp{ + Code: code, + Data: data, + }) +} diff --git a/comet/http/server.go b/comet/http/server.go new file mode 100644 index 0000000..c871fef --- /dev/null +++ b/comet/http/server.go @@ -0,0 +1,46 @@ +package http + +import ( + "github.com/gin-contrib/pprof" + "github.com/gin-gonic/gin" + "github.com/rs/zerolog/log" + "gitlab.33.cn/chat/im/comet" + "gitlab.33.cn/chat/im/comet/conf" + "net/http" +) + +var ( + srv *comet.Comet +) + +func Start(addr string, s *comet.Comet) *http.Server { + srv = s + gin.ForceConsoleColor() + switch conf.Conf.Env { + case conf.DebugMode: + gin.SetMode(gin.DebugMode) + case conf.ReleaseMode: + gin.SetMode(gin.ReleaseMode) + } + engine := gin.Default() + SetupEngine(engine) + pprof.Register(engine) + + srv := &http.Server{ + Addr: addr, + Handler: engine, + } + go func() { + // service connections + if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Fatal().Err(err).Msg("http listen failed") + } + }() + return srv +} + +func SetupEngine(e *gin.Engine) *gin.Engine { + e.GET("/statics", Statics) + e.GET("/groupDetails", GroupDetails) + return e +} diff --git a/comet/http/statics.go b/comet/http/statics.go new file mode 100644 index 0000000..cb16fce --- /dev/null +++ b/comet/http/statics.go @@ -0,0 +1,75 @@ +package http + +import "github.com/gin-gonic/gin" + +func Statics(c *gin.Context) { + var arg struct { + Drop bool `form:"isDrop"` + } + if err := c.BindQuery(&arg); err != nil { + errors(c, RequestErr, err.Error()) + return + } + res := map[string]interface{}{ + "buckets": groupsInfo(arg.Drop), + } + result(c, res, OK) +} + +func groupsInfo(isDrop bool) []map[string]interface{} { + var res = make([]map[string]interface{}, len(srv.Buckets())) + for i, bucket := range srv.Buckets() { + if !isDrop { + if bucket.GroupCount() == 0 { + continue + } + if len(bucket.GroupsCount()) == 0 { + continue + } + } + item := map[string]interface{}{ + "counts": bucket.GroupCount(), + "group-members": bucket.GroupsCount(), + } + res[i] = item + } + return res +} + +func GroupDetails(c *gin.Context) { + var arg struct { + Groups []string `form:"groups" binding:"required"` + } + if err := c.BindQuery(&arg); err != nil { + errors(c, RequestErr, err.Error()) + return + } + + var groups = make([]interface{}, 0) + for _, gid := range arg.Groups { + gInfo := map[string]interface{}{ + "gid": gid, + "members": groupsMembers(gid), + } + groups = append(groups, gInfo) + } + res := map[string]interface{}{ + "groups": groups, + } + result(c, res, OK) +} + +func groupsMembers(gid string) map[string]string { + members := make(map[string]string, 0) + for _, bucket := range srv.Buckets() { + g := bucket.Group(gid) + if g == nil { + continue + } + mems, ips := g.Members() + for i, mem := range mems { + members[mem] = ips[i] + } + } + return members +} diff --git a/comet/nodes.go b/comet/nodes.go new file mode 100644 index 0000000..29d2834 --- /dev/null +++ b/comet/nodes.go @@ -0,0 +1,7 @@ +package comet + +type Node struct { + Current *Channel + Next *Node + Prev *Node +} diff --git a/comet/operation.go b/comet/operation.go new file mode 100644 index 0000000..440651f --- /dev/null +++ b/comet/operation.go @@ -0,0 +1,44 @@ +package comet + +import ( + "context" + "strconv" + + "gitlab.33.cn/chat/im/api/comet/grpc" + "gitlab.33.cn/chat/im/dtask" +) + +// Operate operate. +func (s *Comet) Operate(ctx context.Context, p *grpc.Proto, ch *Channel, tsk *dtask.Task) error { + switch p.Op { + case int32(grpc.Op_SendMsg): + //标明Ack的消息序列 + p.Ack = p.Seq + err := s.Receive(ctx, ch.Key, p) + if err != nil { + //下层业务调用失败,返回error的话会直接断开连接 + return err + } + //p.Op = int32(grpc.Op_SendMsgReply) + case int32(grpc.Op_ReceiveMsgReply): + //从task中删除某一条 + if j := tsk.Get(strconv.FormatInt(int64(p.Ack), 10)); j != nil { + j.Cancel() + } + err := s.Receive(ctx, ch.Key, p) + if err != nil { + //下层业务调用失败,返回error的话会直接断开连接 + return err + } + case int32(grpc.Op_SyncMsgReq): + err := s.Receive(ctx, ch.Key, p) + if err != nil { + //下层业务调用失败,返回error的话会直接断开连接 + return err + } + p.Op = int32(grpc.Op_SyncMsgReply) + default: + return s.Receive(ctx, ch.Key, p) + } + return nil +} diff --git a/comet/ring.go b/comet/ring.go new file mode 100644 index 0000000..a13642e --- /dev/null +++ b/comet/ring.go @@ -0,0 +1,80 @@ +package comet + +import ( + "gitlab.33.cn/chat/im/api/comet/grpc" + "gitlab.33.cn/chat/im/comet/errors" +) + +// Ring ring proto buffer. +type Ring struct { + // read + rp uint64 + num uint64 + mask uint64 + // TODO split cacheline, many cpu cache line size is 64 + // pad [40]byte + // write + wp uint64 + data []grpc.Proto +} + +// NewRing new a ring buffer. +func NewRing(num int) *Ring { + r := new(Ring) + r.init(uint64(num)) + return r +} + +// Init init ring. +func (r *Ring) Init(num int) { + r.init(uint64(num)) +} + +func (r *Ring) init(num uint64) { + // 2^N + if num&(num-1) != 0 { + for num&(num-1) != 0 { + num &= (num - 1) + } + num = num << 1 + } + r.data = make([]grpc.Proto, num) + r.num = num + r.mask = r.num - 1 +} + +// Get get a proto from ring. +func (r *Ring) Get() (proto *grpc.Proto, err error) { + if r.rp == r.wp { + return nil, errors.ErrRingEmpty + } + proto = &r.data[r.rp&r.mask] + return +} + +// GetAdv incr read index. +func (r *Ring) GetAdv() { + r.rp++ +} + +// Set get a proto to write. +func (r *Ring) Set() (proto *grpc.Proto, err error) { + if r.wp-r.rp >= r.num { + return nil, errors.ErrRingFull + } + proto = &r.data[r.wp&r.mask] + return +} + +// SetAdv incr write index. +func (r *Ring) SetAdv() { + r.wp++ +} + +// Reset reset ring. +func (r *Ring) Reset() { + r.rp = 0 + r.wp = 0 + // prevent pad compiler optimization + // r.pad = [40]byte{} +} diff --git a/comet/round.go b/comet/round.go new file mode 100644 index 0000000..2f867e2 --- /dev/null +++ b/comet/round.go @@ -0,0 +1,78 @@ +package comet + +import ( + "github.com/Terry-Mao/goim/pkg/bytes" + "github.com/Terry-Mao/goim/pkg/time" + "gitlab.33.cn/chat/im/comet/conf" +) + +// RoundOptions round options. +type RoundOptions struct { + Timer int + TimerSize int + Reader int + ReadBuf int + ReadBufSize int + Writer int + WriteBuf int + WriteBufSize int + Task int + TaskSize int +} + +// Round userd for connection round-robin get a reader/writer/timer for split big lock. +type Round struct { + readers []bytes.Pool + writers []bytes.Pool + timers []time.Timer + options RoundOptions +} + +// NewRound new a round struct. +func NewRound(c *conf.Config) (r *Round) { + var i int + r = &Round{ + options: RoundOptions{ + Reader: c.TCP.Reader, + ReadBuf: c.TCP.ReadBuf, + ReadBufSize: c.TCP.ReadBufSize, + Writer: c.TCP.Writer, + WriteBuf: c.TCP.WriteBuf, + WriteBufSize: c.TCP.WriteBufSize, + Timer: c.Protocol.Timer, + TimerSize: c.Protocol.TimerSize, + Task: c.Protocol.Task, + TaskSize: c.Protocol.TaskSize, + }} + // reader + r.readers = make([]bytes.Pool, r.options.Reader) + for i = 0; i < r.options.Reader; i++ { + r.readers[i].Init(r.options.ReadBuf, r.options.ReadBufSize) + } + // writer + r.writers = make([]bytes.Pool, r.options.Writer) + for i = 0; i < r.options.Writer; i++ { + r.writers[i].Init(r.options.WriteBuf, r.options.WriteBufSize) + } + // timer + r.timers = make([]time.Timer, r.options.Timer) + for i = 0; i < r.options.Timer; i++ { + r.timers[i].Init(r.options.TimerSize) + } + return +} + +// Timer get a timer. +func (r *Round) Timer(rn int) *time.Timer { + return &(r.timers[rn%r.options.Timer]) +} + +// Reader get a reader memory buffer. +func (r *Round) Reader(rn int) *bytes.Pool { + return &(r.readers[rn%r.options.Reader]) +} + +// Writer get a writer memory buffer pool. +func (r *Round) Writer(rn int) *bytes.Pool { + return &(r.writers[rn%r.options.Writer]) +} diff --git a/comet/tcp.go b/comet/tcp.go new file mode 100644 index 0000000..3d8d100 --- /dev/null +++ b/comet/tcp.go @@ -0,0 +1,304 @@ +package comet + +import ( + "context" + "fmt" + "io" + "net" + "strconv" + "strings" + "time" + + "github.com/Terry-Mao/goim/pkg/bufio" + "github.com/Terry-Mao/goim/pkg/bytes" + xtime "github.com/Terry-Mao/goim/pkg/time" + "github.com/golang/protobuf/proto" + "github.com/rs/zerolog/log" + "gitlab.33.cn/chat/im/api/comet/grpc" + "gitlab.33.cn/chat/im/dtask" +) + +// InitTCP listen all tcp.bind and start accept connections. +func InitTCP(server *Comet, addrs []string, accept int) (err error) { + var ( + bind string + listener *net.TCPListener + addr *net.TCPAddr + ) + for _, bind = range addrs { + if addr, err = net.ResolveTCPAddr("tcp", bind); err != nil { + log.Error().Stack().Err(err).Msg(fmt.Sprintf("net.ResolveTCPAddr(tcp, %s)", bind)) + return + } + if listener, err = net.ListenTCP("tcp", addr); err != nil { + log.Error().Stack().Err(err).Msg(fmt.Sprintf("net.ListenTCP(tcp, %s)", bind)) + return + } + log.Info().Str("bind", bind).Msg("start tcp listen") + // split N core accept + for i := 0; i < accept; i++ { + go acceptTCP(server, listener) + } + } + return +} + +// Accept accepts connections on the listener and serves requests +// for each incoming connection. Accept blocks; the caller typically +// invokes it in a go statement. +func acceptTCP(server *Comet, lis *net.TCPListener) { + var ( + conn *net.TCPConn + err error + r int + ) + for { + if conn, err = lis.AcceptTCP(); err != nil { + // if listener close then return + log.Error().Stack().Err(err).Msg(fmt.Sprintf("listener.Accept(\"%s\")", lis.Addr().String())) + continue + //return + } + log.Info().Str("remoteIP", conn.RemoteAddr().String()).Msg("accept tcp conn") + if err = conn.SetKeepAlive(server.c.TCP.KeepAlive); err != nil { + log.Error().Stack().Err(err).Msg("conn.SetKeepAlive()") + return + } + if err = conn.SetReadBuffer(server.c.TCP.Rcvbuf); err != nil { + log.Error().Stack().Err(err).Msg("conn.SetReadBuffer()") + return + } + if err = conn.SetWriteBuffer(server.c.TCP.Sndbuf); err != nil { + log.Error().Stack().Err(err).Msg("conn.SetWriteBuffer()") + return + } + go serveTCP(server, conn, r) + if r++; r == maxInt { + r = 0 + } + } +} + +func serveTCP(s *Comet, conn *net.TCPConn, r int) { + var ( + // timer + tr = s.round.Timer(r) + rp = s.round.Reader(r) + wp = s.round.Writer(r) + ) + s.ServeTCP(conn, rp, wp, tr) +} + +// ServeTCP serve a tcp connection. +func (s *Comet) ServeTCP(conn *net.TCPConn, rp, wp *bytes.Pool, tr *xtime.Timer) { + var ( + err error + hb time.Duration + p *grpc.Proto + b *Bucket + trd *xtime.TimerData + lastHb = time.Now() + rb = rp.Get() + wb = wp.Get() + ch = NewChannel(s.c.Protocol.CliProto, s.c.Protocol.SvrProto) + rr = &ch.Reader + wr = &ch.Writer + tsk *dtask.Task + ) + ch.Reader.ResetBuffer(conn, rb.Bytes()) + ch.Writer.ResetBuffer(conn, wb.Bytes()) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + // handshake + step := 0 + trd = tr.Add(time.Duration(s.c.Protocol.HandshakeTimeout), func() { + conn.Close() + log.Error().Int("step", step).Str("key", ch.Key).Str("remoteIP", conn.RemoteAddr().String()).Msg("tcp handshake timeout") + }) + ch.IP, ch.Port, _ = net.SplitHostPort(conn.RemoteAddr().String()) + // must not setadv, only used in auth + step = 1 + if p, err = ch.CliProto.Set(); err == nil { + if ch.Key, hb, err = s.authTCP(ctx, rr, wr, p); err == nil { + log.Info().Str("key", ch.Key).Str("remoteIP", conn.RemoteAddr().String()).Msg("authoried") + b = s.Bucket(ch.Key) + err = b.Put(ch) + } + } + step = 2 + if err != nil { + conn.Close() + rp.Put(rb) + wp.Put(wb) + tr.Del(trd) + log.Error().Str("key", ch.Key).Err(err).Msg("handshake failed") + return + } + trd.Key = ch.Key + tr.Set(trd, hb) + step = 3 + // hanshake ok start dispatch goroutine + tsk = dtask.NewTask() + go s.dispatchTCP(conn, wr, wp, wb, ch, tsk) + serverHeartbeat := s.RandServerHearbeat() + for { + if p, err = ch.CliProto.Set(); err != nil { + break + } + if err = p.ReadTCP(rr); err != nil { + log.Info().Err(err).Msg("ReadTCP failed") + break + } + if p.Op == int32(grpc.Op_Heartbeat) { + tr.Set(trd, hb) + p.Op = int32(grpc.Op_HeartbeatReply) + p.Body = nil + // NOTE: send server heartbeat for a long time + if now := time.Now(); now.Sub(lastHb) > serverHeartbeat { + if err1 := s.Heartbeat(ctx, ch.Key); err1 == nil { + lastHb = now + } + } + step++ + } else { + if err = s.Operate(ctx, p, ch, tsk); err != nil { + break + } + } + // msg sent from client will be dispatched to client itself + ch.CliProto.SetAdv() + ch.Signal() + } + if err != nil && err != io.EOF && !strings.Contains(err.Error(), "closed") { + log.Error().Str("key", ch.Key).Err(err).Msg("server tcp failed") + } + b.Del(ch) + tr.Del(trd) + rp.Put(rb) + conn.Close() + ch.Close() + if err = s.Disconnect(ctx, ch.Key); err != nil { + log.Error().Str("key", ch.Key).Err(err).Msg("operator do disconnect") + } +} + +// dispatch accepts connections on the listener and serves requests +// for each incoming connection. dispatch blocks; the caller typically +// invokes it in a go statement. +func (s *Comet) dispatchTCP(conn *net.TCPConn, wr *bufio.Writer, wp *bytes.Pool, wb *bytes.Buffer, ch *Channel, tsk *dtask.Task) { + var ( + err error + finish bool + online int32 + ) + for { + var p = ch.Ready() + switch p { + case grpc.ProtoFinish: + finish = true + goto failed + case grpc.ProtoReady: + // fetch message from svrbox(client send) + for { + if p, err = ch.CliProto.Get(); err != nil { + break + } + if p.Op == int32(grpc.Op_HeartbeatReply) { + if err = p.WriteTCPHeart(wr, online); err != nil { + goto failed + } + } else if p.Op == int32(grpc.Op_ReceiveMsgReply) { + //skip + } else if p.Op == int32(grpc.Op_SendMsg) { + //skip + } else { + if err = p.WriteTCP(wr); err != nil { + goto failed + } + } + p.Body = nil // avoid memory leak + ch.CliProto.GetAdv() + } + default: + switch p.Op { + case int32(grpc.Op_SendMsgReply): + if err = p.WriteTCP(wr); err != nil { + goto failed + } + case int32(grpc.Op_RePush): + pro := proto.Clone(p) + if p, ok := pro.(*grpc.Proto); ok { + p.Op = int32(grpc.Op_ReceiveMsg) + if err = p.WriteTCP(wr); err != nil { + goto failed + } + } else { + log.Error().Msg("proto can`t clone Proto") + } + case int32(grpc.Op_ReceiveMsg): + if err = p.WriteTCP(wr); err != nil { + goto failed + } + p.Op = int32(grpc.Op_RePush) + seq := strconv.FormatInt(int64(p.Seq), 10) + if j := tsk.Get(seq); j != nil { + continue + } + //push into task pool + job, inserted := tsk.AddJobRepeat(time.Second*5, 0, func() { + if _, err = ch.Push(p); err != nil { + log.Error().Err(err).Msg("task job ch.Push error") + return + } + }) + if !inserted { + log.Error().Err(err).Msg("tsk.AddJobRepeat error") + goto failed + } + tsk.Add(seq, job) + default: + continue + } + } + // only hungry flush response + if err = wr.Flush(); err != nil { + break + } + } +failed: + if err != nil { + log.Error().Str("key", ch.Key).Err(err).Msg("dispatch tcp failed") + } + tsk.Stop() + conn.Close() + wp.Put(wb) + // must ensure all channel message discard, for reader won't blocking Signal + for !finish { + finish = (ch.Ready() == grpc.ProtoFinish) + } +} + +// auth for goim handshake with client, use rsa & aes. +func (s *Comet) authTCP(ctx context.Context, rr *bufio.Reader, wr *bufio.Writer, p *grpc.Proto) (key string, hb time.Duration, err error) { + for { + if err = p.ReadTCP(rr); err != nil { + return + } + if p.Op == int32(grpc.Op_Auth) { + break + } else { + log.Error().Int32("option", p.Op).Msg("tcp request option not auth") + } + } + if key, hb, err = s.Connect(ctx, p); err != nil { + log.Error().Err(err).Msg("can not call logic.Connect") + return + } + p.Op = int32(grpc.Op_AuthReply) + p.Body = nil + if err = p.WriteTCP(wr); err != nil { + return + } + err = wr.Flush() + return +} diff --git a/comet/ws.go b/comet/ws.go new file mode 100644 index 0000000..cb64a23 --- /dev/null +++ b/comet/ws.go @@ -0,0 +1,341 @@ +package comet + +import ( + "context" + "fmt" + "io" + "net" + "runtime" + "strconv" + "strings" + "time" + + "github.com/Terry-Mao/goim/pkg/bytes" + xtime "github.com/Terry-Mao/goim/pkg/time" + "github.com/Terry-Mao/goim/pkg/websocket" + "github.com/rs/zerolog/log" + "gitlab.33.cn/chat/im/api/comet/grpc" + "gitlab.33.cn/chat/im/dtask" +) + +const ( + maxInt = 1<<31 - 1 +) + +// InitWebsocket listen all tcp.bind and start accept connections. +func InitWebsocket(server *Comet, addrs []string, accept int) (err error) { + var ( + bind string + listener *net.TCPListener + addr *net.TCPAddr + ) + for _, bind = range addrs { + if addr, err = net.ResolveTCPAddr("tcp", bind); err != nil { + log.Error().Stack().Err(err).Msg(fmt.Sprintf("net.ResolveTCPAddr(tcp, %s)", bind)) + return + } + if listener, err = net.ListenTCP("tcp", addr); err != nil { + log.Error().Stack().Err(err).Msg(fmt.Sprintf("net.ListenTCP(tcp, %s)", bind)) + return + } + log.Info().Str("bind", bind).Msg("start ws listen") + // split N core accept + for i := 0; i < accept; i++ { + go acceptWebsocket(server, listener) + } + } + return +} + +// Accept accepts connections on the listener and serves requests +// for each incoming connection. Accept blocks; the caller typically +// invokes it in a go statement. +func acceptWebsocket(server *Comet, lis *net.TCPListener) { + defer func() { + buf := make([]byte, 1024*3) + runtime.Stack(buf, false) + log.Error().Str("panic", string(buf)).Msg("acceptWebsocket done") + if r := recover(); r != nil { + buf := make([]byte, 1024*3) + runtime.Stack(buf, false) + log.Error().Interface("recover", r).Str("panic", string(buf)).Msg("Recovered in acceptWebsocket") + } + }() + var ( + conn *net.TCPConn + err error + r int + ) + for { + if conn, err = lis.AcceptTCP(); err != nil { + // if listener close then return + log.Error().Stack().Err(err).Msg(fmt.Sprintf("listener.Accept(\"%s\")", lis.Addr().String())) + continue + //return + } + log.Info().Str("remoteIP", conn.RemoteAddr().String()).Msg("accept ws conn") + if err = conn.SetKeepAlive(server.c.TCP.KeepAlive); err != nil { + log.Error().Stack().Err(err).Msg("conn.SetKeepAlive()") + return + } + if err = conn.SetReadBuffer(server.c.TCP.Rcvbuf); err != nil { + log.Error().Stack().Err(err).Msg("conn.SetReadBuffer()") + return + } + if err = conn.SetWriteBuffer(server.c.TCP.Sndbuf); err != nil { + log.Error().Stack().Err(err).Msg("conn.SetWriteBuffer()") + return + } + go serveWebsocket(server, conn, r) + if r++; r == maxInt { + r = 0 + } + } +} + +func serveWebsocket(s *Comet, conn net.Conn, r int) { + var ( + // timer + tr = s.round.Timer(r) + rp = s.round.Reader(r) + wp = s.round.Writer(r) + ) + s.ServeWebsocket(conn, rp, wp, tr) +} + +// ServeWebsocket serve a websocket connection. +func (s *Comet) ServeWebsocket(conn net.Conn, rp, wp *bytes.Pool, tr *xtime.Timer) { + var ( + err error + hb time.Duration + p *grpc.Proto + b *Bucket + trd *xtime.TimerData + lastHB = time.Now() + rb = rp.Get() + ch = NewChannel(s.c.Protocol.CliProto, s.c.Protocol.SvrProto) + rr = &ch.Reader + wr = &ch.Writer + ws *websocket.Conn // websocket + req *websocket.Request + tsk *dtask.Task + ) + // reader + ch.Reader.ResetBuffer(conn, rb.Bytes()) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + // handshake + step := 0 + trd = tr.Add(time.Duration(s.c.Protocol.HandshakeTimeout), func() { + // NOTE: fix close block for tls + _ = conn.SetDeadline(time.Now().Add(time.Millisecond * 100)) + _ = conn.Close() + log.Error().Int("step", step).Str("key", ch.Key).Str("remoteIP", conn.RemoteAddr().String()).Msg("ws handshake timeout") + }) + // websocket + ch.IP, ch.Port, _ = net.SplitHostPort(conn.RemoteAddr().String()) + step = 1 + if req, err = websocket.ReadRequest(rr); err != nil || req.RequestURI != "/sub" { + conn.Close() + tr.Del(trd) + rp.Put(rb) + if err != io.EOF { + log.Error().Err(err).Msg("http.ReadRequest(rr)") + } + return + } + // writer + wb := wp.Get() + ch.Writer.ResetBuffer(conn, wb.Bytes()) + step = 2 + if ws, err = websocket.Upgrade(conn, rr, wr, req); err != nil { + conn.Close() + tr.Del(trd) + rp.Put(rb) + wp.Put(wb) + if err != io.EOF { + log.Error().Err(err).Msg("websocket.NewServerConn") + } + return + } + // must not setadv, only used in auth + step = 3 + if p, err = ch.CliProto.Set(); err == nil { + if ch.Key, hb, err = s.authWebsocket(ctx, ws, p); err == nil { + log.Info().Str("key", ch.Key).Str("remoteIP", conn.RemoteAddr().String()).Msg("authoried") + b = s.Bucket(ch.Key) + err = b.Put(ch) + } + } + step = 4 + if err != nil { + ws.Close() + rp.Put(rb) + wp.Put(wb) + tr.Del(trd) + if err != io.EOF && err != websocket.ErrMessageClose { + log.Error().Int("step", step).Str("key", ch.Key).Str("remoteIP", conn.RemoteAddr().String()).Msg("ws handshake failed") + } + return + } + trd.Key = ch.Key + tr.Set(trd, hb) + // hanshake ok start dispatch goroutine + step = 5 + tsk = dtask.NewTask() + go s.dispatchWebsocket(ws, wp, wb, ch, tsk) + serverHeartbeat := s.RandServerHearbeat() + for { + if p, err = ch.CliProto.Set(); err != nil { + break + } + if err = p.ReadWebsocket(ws); err != nil { + break + } + if p.Op == int32(grpc.Op_Heartbeat) { + tr.Set(trd, hb) + p.Op = int32(grpc.Op_HeartbeatReply) + p.Body = nil + // NOTE: send server heartbeat for a long time + if now := time.Now(); now.Sub(lastHB) > serverHeartbeat { + if err1 := s.Heartbeat(ctx, ch.Key); err1 == nil { + lastHB = now + } + } + step++ + } else { + if err = s.Operate(ctx, p, ch, tsk); err != nil { + break + } + } + ch.CliProto.SetAdv() + ch.Signal() + } + if err != nil && err != io.EOF && err != websocket.ErrMessageClose && !strings.Contains(err.Error(), "closed") { + log.Error().Err(err).Str("key", ch.Key).Msg("server ws failed") + } + b.Del(ch) + tr.Del(trd) + ws.Close() + ch.Close() + rp.Put(rb) + if err = s.Disconnect(ctx, ch.Key); err != nil { + log.Error().Err(err).Str("key", ch.Key).Msg("operator do disconnect") + } +} + +// dispatch accepts connections on the listener and serves requests +// for each incoming connection. dispatch blocks; the caller typically +// invokes it in a go statement. +func (s *Comet) dispatchWebsocket(ws *websocket.Conn, wp *bytes.Pool, wb *bytes.Buffer, ch *Channel, tsk *dtask.Task) { + var ( + err error + finish bool + online int32 + ) + for { + var p = ch.Ready() + switch p { + case grpc.ProtoFinish: + finish = true + goto failed + case grpc.ProtoReady: + // fetch message from svrbox(client send) + for { + if p, err = ch.CliProto.Get(); err != nil { + break + } + if p.Op == int32(grpc.Op_HeartbeatReply) { + if err = p.WriteWebsocketHeart(ws, online); err != nil { + goto failed + } + } else if p.Op == int32(grpc.Op_ReceiveMsgReply) { + //skip + } else if p.Op == int32(grpc.Op_SendMsg) { + //skip + } else { + if err = p.WriteWebsocket(ws); err != nil { + goto failed + } + } + p.Body = nil // avoid memory leak + ch.CliProto.GetAdv() + } + default: + switch p.Op { + case int32(grpc.Op_SendMsgReply): + if err = p.WriteWebsocket(ws); err != nil { + goto failed + } + case int32(grpc.Op_RePush): + p.Op = int32(grpc.Op_ReceiveMsg) + if err = p.WriteWebsocket(ws); err != nil { + goto failed + } + case int32(grpc.Op_ReceiveMsg): + if err = p.WriteWebsocket(ws); err != nil { + goto failed + } + p.Op = int32(grpc.Op_RePush) + seq := strconv.FormatInt(int64(p.Seq), 10) + if j := tsk.Get(seq); j != nil { + continue + } + //push into task pool + job, inserted := tsk.AddJobRepeat(time.Second*5, 0, func() { + if _, err = ch.Push(p); err != nil { + log.Error().Err(err).Msg("task job ch.Push error") + return + } + }) + if !inserted { + log.Error().Err(err).Msg("tsk.AddJobRepeat error") + goto failed + } + tsk.Add(seq, job) + default: + continue + } + } + // only hungry flush response + if err = ws.Flush(); err != nil { + break + } + } +failed: + if err != nil && err != io.EOF && err != websocket.ErrMessageClose { + log.Error().Err(err).Str("key", ch.Key).Msg("dispatch ws error") + } + tsk.Stop() + ws.Close() + wp.Put(wb) + // must ensure all channel message discard, for reader won't blocking Signal + for !finish { + finish = (ch.Ready() == grpc.ProtoFinish) + } +} + +// auth for goim handshake with client, use rsa & aes. +func (s *Comet) authWebsocket(ctx context.Context, ws *websocket.Conn, p *grpc.Proto) (key string, hb time.Duration, err error) { + for { + if err = p.ReadWebsocket(ws); err != nil { + return + } + if p.Op == int32(grpc.Op_Auth) { + break + } else { + log.Error().Int32("operation", p.Op).Msg("ws request operation not auth") + } + } + if key, hb, err = s.Connect(ctx, p); err != nil { + log.Error().Err(err).Msg("can not call logic.Connect") + return + } + p.Op = int32(grpc.Op_AuthReply) + p.Body = nil + if err = p.WriteWebsocket(ws); err != nil { + return + } + err = ws.Flush() + return +} diff --git a/common/grpc.go b/common/grpc.go new file mode 100644 index 0000000..7e79b8e --- /dev/null +++ b/common/grpc.go @@ -0,0 +1,66 @@ +package common + +import ( + "context" + "fmt" + "time" + + xkey "gitlab.33.cn/chat/im/naming/balancer/key" + "google.golang.org/grpc" + "google.golang.org/grpc/balancer/roundrobin" + "google.golang.org/grpc/keepalive" +) + +const ( + grpcInitialWindowSize = 1 << 24 + grpcInitialConnWindowSize = 1 << 24 + grpcMaxSendMsgSize = 1 << 24 + grpcMaxCallMsgSize = 1 << 24 + grpcKeepAliveTime = time.Second * 10 + grpcKeepAliveTimeout = time.Second * 3 + grpcBackoffMaxDelay = time.Second * 3 +) + +func NewGRPCConn(addr string, timeout time.Duration, opts ...grpc.DialOption) (*grpc.ClientConn, error) { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + dialOpts := []grpc.DialOption{ + grpc.WithInsecure(), + grpc.WithInitialWindowSize(grpcInitialWindowSize), + grpc.WithInitialConnWindowSize(grpcInitialConnWindowSize), + grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(grpcMaxCallMsgSize)), + grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(grpcMaxSendMsgSize)), + grpc.WithBackoffMaxDelay(grpcBackoffMaxDelay), + grpc.WithKeepaliveParams(keepalive.ClientParameters{ + Time: grpcKeepAliveTime, + Timeout: grpcKeepAliveTimeout, + PermitWithoutStream: true, + }), + grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, roundrobin.Name)), + } + dialOpts = append(dialOpts, opts...) + return grpc.DialContext(ctx, addr, dialOpts...) +} + +func NewGRPCConnWithKey(addr string, timeout time.Duration, opts ...grpc.DialOption) (*grpc.ClientConn, error) { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + dialOpts := []grpc.DialOption{ + grpc.WithInsecure(), + grpc.WithInitialWindowSize(grpcInitialWindowSize), + grpc.WithInitialConnWindowSize(grpcInitialConnWindowSize), + grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(grpcMaxCallMsgSize)), + grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(grpcMaxSendMsgSize)), + grpc.WithBackoffMaxDelay(grpcBackoffMaxDelay), + grpc.WithKeepaliveParams(keepalive.ClientParameters{ + Time: grpcKeepAliveTime, + Timeout: grpcKeepAliveTimeout, + PermitWithoutStream: true, + }), + grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, xkey.Name)), + } + dialOpts = append(dialOpts, opts...) + return grpc.DialContext(ctx, addr, dialOpts...) +} diff --git a/docs/goim-arch.png b/docs/goim-arch.png new file mode 100644 index 0000000..d12a9bb Binary files /dev/null and b/docs/goim-arch.png differ diff --git a/docs/proto.md b/docs/proto.md new file mode 100644 index 0000000..f0f88a8 --- /dev/null +++ b/docs/proto.md @@ -0,0 +1,63 @@ +# comet 客户端通讯协议 + +`comet` 支持两种协议和客户端通讯 `websocket`,`tcp`。 + +**请求URL** + +``` +ws://DOMAIN/sub/ +tcp://DOMAIN +``` + +**协议格式** + +二进制,请求和返回协议一致 + +**请求&返回参数** + +| 参数名 | 必选 | 类型 | 说明 | +| :----- | :--- | :--- | :--- | +| package length | true | int32 bigendian | 包长度 | +| header Length | true | int16 bigendian | 包头长度 | +| ver | true | int16 bigendian | 协议版本 | +| operation | true | int32 bigendian | 协议指令 | +| seq | true | int32 bigendian | 序列号 | +| ack | true | int32 bigendian | 确认号 | +| body | false | binary | $(package lenth) - $(header length) | + +## 指令 +| 指令 | 说明 | +| :----- | :--- | +| 1 | 客户端连接认证 | +| 2 | 客户端连接认证响应 | +| 3 | 客户端请求心跳 | +| 4 | 服务端心跳答复 | +| 5 | 客户端断开连接 | +| 6 | 客户端断开连接响应 | +| 7 | 客户端发送消息 | +| 8 | 客户端发送消息响应 | +| 9 | 客户端接收消息 | +| 10 | 客户端接收消息响应 | +| 14 | 多端同步指令(未使用) | +| 15 | 多端同步指令响应(未使用) | + + +### 连接认证 + +operation 为 1 时, body 必须可以反序列化为 AuthMsg + +``` +message AuthMsg { + string appId = 1; + string token = 2; + bytes ext = 3; // 其它业务方可能需要的信息 +} +``` + +operation 为 15 时, body 必须可以反序列化为 SyncMsg +``` +message SyncMsg { + int64 logId = 1; +} +``` +具体协议参考 `api/comet/grpc/comet.proto` 和 `api/logic/grpc/comet.proto` \ No newline at end of file diff --git a/dtask/clock.go b/dtask/clock.go new file mode 100644 index 0000000..b037505 --- /dev/null +++ b/dtask/clock.go @@ -0,0 +1,327 @@ +// Package clock is a low consumption, low latency support for frequent updates of large capacity timing manager: +// 1、能够添加一次性、重复性任务,并能在其执行前撤销或频繁更改。 +// 2、支持同一时间点,多个任务提醒。 +// 3、适用于中等密度,大跨度的单次、多次定时任务。 +// 4、支持10万次/秒的定时任务执行、提醒、撤销或添加操作,平均延迟10微秒内 +// 5、支持注册任务的函数调用,及事件通知。 +// 基本处理逻辑: +// 1、重复性任务,流程是: +// a、注册重复任务 +// b、时间抵达时,控制器调用注册函数,并发送通知 +// c、如果次数达到限制,则撤销;否则,控制器更新该任务的下次执行时间点 +// d、控制器等待下一个最近需要执行的任务 +// 2、一次性任务,可以是服务运行时,当前时间点之后的任意事件,流程是: +// a、注册一次性任务 +// b、时间抵达时,控制器调用注册函数,并发送通知 +// c、控制器释放该任务 +// d、控制器等待下一个最近需要执行的任务 +// 使用方式,参见示例代码。 +package dtask + +import ( + "gitlab.33.cn/chat/im/dtask/pkg/rbtree" + "math" + "sync" + "sync/atomic" + "time" +) + +/* +This code is based on the following resources: +source code: https://github.com/alex023/clock.git +*/ +const _UNTOUCHED = time.Duration(math.MaxInt64) + +var ( + defaultClock *Clock + oncedo sync.Once +) + +//Default return singal default clock +func Default() *Clock { + oncedo.Do(initClock) + return defaultClock +} +func initClock() { + defaultClock = NewClock() +} + +// Clock is jobs schedule +type Clock struct { + seq uint64 + count uint64 //已执行次数,不得大于times + waitJobsNum uint64 //num of jobs which wait for action + jobQueue *rbtree.Rbtree //inner memory storage + pauseChan chan struct{} + resumeChan chan struct{} + exitChan chan struct{} +} + +var singal = struct{}{} + +//NewClock Create a task queue controller +func NewClock() *Clock { + c := &Clock{ + jobQueue: rbtree.New(), + pauseChan: make(chan struct{}, 0), + resumeChan: make(chan struct{}, 0), + exitChan: make(chan struct{}, 0), + } + + c.start() + + return c +} +func (jl *Clock) start() { + now := time.Now() + untouchedJob := jobItem{ + createTime: now, + intervalTime: time.Duration(math.MaxInt64), + fn: func() { + //this jobItem is untouched. + }, + } + + _, inserted := jl.addJob(now, untouchedJob.intervalTime, 1, untouchedJob.fn) + if !inserted { + panic("[clock] internal error.Reason cannot insert job.") + } + //开启守护协程 + go jl.schedule() + jl.resume() +} + +func (jl *Clock) pause() { + jl.pauseChan <- singal +} +func (jl *Clock) resume() { + jl.resumeChan <- singal +} +func (jl *Clock) exit() { + jl.exitChan <- singal +} + +func (jl *Clock) immediate() { + for { + if item := jl.jobQueue.Min(); item != nil { + atomic.AddUint64(&jl.count, 1) + + job := item.(*jobItem) + job.action(false) + + jl.removeJob(job) + + } else { + break + } + } +} + +func (jl *Clock) schedule() { + var ( + timeout time.Duration + job *jobItem + timer = newSafeTimer(_UNTOUCHED) + ) + defer timer.Stop() +Pause: + <-jl.resumeChan + for { + job, _ = jl.jobQueue.Min().(*jobItem) //ignore ok-assert + timeout = job.actionTime.Sub(time.Now()) + timer.SafeReset(timeout) + select { + case <-timer.C: + timer.SCR() + + atomic.AddUint64(&jl.count, 1) + + job.action(true) + + if job.actionMax == 0 || job.actionMax > job.actionCount { + jl.jobQueue.Delete(job) + job.actionTime = job.actionTime.Add(job.intervalTime) + jl.jobQueue.Insert(job) + } else { + jl.removeJob(job) + } + case <-jl.pauseChan: + goto Pause + case <-jl.exitChan: + goto Exit + } + } +Exit: +} + +// UpdateJobTimeout update a timed task with time duration after now +// @job: job identifier +// @actionTime: new job schedule time,must be greater than 0 +func (jl *Clock) UpdateJobTimeout(job Job, actionTime time.Duration) (updated bool) { + if job == nil || actionTime.Nanoseconds() <= 0 || !job.isAvailable() { + return false + } + now := time.Now() + + item, ok := job.(*jobItem) + if !ok { + return false + } + jl.pause() + defer jl.resume() + + // update jobitem in job queue + jl.jobQueue.Delete(item) + item.actionTime = now.Add(actionTime) + jl.jobQueue.Insert(item) + + updated = true + return +} + +// AddJobWithInterval insert a timed task with time duration after now +// @actionTime: Duration after now +// @jobFunc: Callback function,not nil +// return +// @jobScheduled: A reference to a task that has been scheduled. +func (jl *Clock) AddJobWithInterval(actionInterval time.Duration, jobFunc func()) (jobScheduled Job, inserted bool) { + if jobFunc == nil || actionInterval.Nanoseconds() <= 0 { + return + } + now := time.Now() + + jl.pause() + jobScheduled, inserted = jl.addJob(now, actionInterval, 1, jobFunc) + jl.resume() + + return +} + +// AddJobWithDeadtime insert a timed task with time point after now +// @actionTime: Execution start time. must after now +// @jobFunc: Callback function,not nil +// return +// @jobScheduled : A reference to a task that has been scheduled. +// @inserted : return false ,if actionTime before time.Now or jobFunc is nil +func (jl *Clock) AddJobWithDeadtime(actionTime time.Time, jobFunc func()) (jobScheduled Job, inserted bool) { + actionInterval := actionTime.Sub(time.Now()) + if jobFunc == nil || actionInterval.Nanoseconds() <= 0 { + return + } + now := time.Now() + + jl.pause() + jobScheduled, inserted = jl.addJob(now, actionInterval, 1, jobFunc) + jl.resume() + + return +} + +// AddJobRepeat add a repeat task with interval duration +// @interval: The interval between two actions of the job +// @actionMax: The number of job execution +// @jobFunc: Callback function,not nil +// return +// @jobScheduled : A reference to a task that has been scheduled. +// @inserted : return false ,if interval is not Positiveor jobFunc is nil +//Note: +// when jobTimes==0,the job will be executed without limitation。If you no longer use, be sure to call the DelJob method to release +func (jl *Clock) AddJobRepeat(interval time.Duration, actionMax uint64, jobFunc func()) (jobScheduled Job, inserted bool) { + if jobFunc == nil || interval.Nanoseconds() <= 0 { + return + } + now := time.Now() + + jl.pause() + jobScheduled, inserted = jl.addJob(now, interval, actionMax, jobFunc) + jl.resume() + + return +} + +func (jl *Clock) addJob(createTime time.Time, actionInterval time.Duration, actionMax uint64, jobFunc func()) (job *jobItem, inserted bool) { + jl.seq++ + jl.waitJobsNum++ + job = &jobItem{ + id: jl.seq, + actionMax: actionMax, + createTime: createTime, + actionTime: createTime.Add(actionInterval), + intervalTime: actionInterval, + msgChan: make(chan Job, 10), + fn: jobFunc, + clock: jl, + } + jl.jobQueue.Insert(job) + inserted = true + + return + +} + +func (jl *Clock) removeJob(item *jobItem) { + if jl.jobQueue.Delete(item) != nil { + jl.waitJobsNum-- + + //job.Cancel --> rmJob -->removeJob; schedule -->removeJob + //it is call repeatly when Job.Cancel + if atomic.CompareAndSwapInt32(&item.cancelFlag, 0, 1) { + item.innerCancel() + } + } +} + +func (jl *Clock) rmJob(job *jobItem) { + jl.pause() + defer jl.resume() + + jl.removeJob(job) + return +} + +// Count 已经执行的任务数。对于重复任务,会计算多次 +func (jl *Clock) Count() uint64 { + return atomic.LoadUint64(&jl.count) +} + +//重置Clock的内部状态 +func (jl *Clock) Reset() *Clock { + jl.exit() + jl.count = 0 + + jl.cleanJobs() + jl.start() + return jl +} + +func (jl *Clock) cleanJobs() { + item := jl.jobQueue.Min() + for item != nil { + job, ok := item.(*jobItem) + if ok { + jl.removeJob(job) + } + item = jl.jobQueue.Min() + } +} + +//WaitJobs get how much jobs waiting for call +func (jl *Clock) WaitJobs() uint64 { + jobs := atomic.LoadUint64(&jl.waitJobsNum) - 1 + return jobs +} + +//Stop stop clock , and cancel all waiting jobs +func (jl *Clock) Stop() { + jl.exit() + + jl.cleanJobs() +} + +//StopGracefull stop clock ,and do once every waiting job including Once\Reapeat +//Note:对于任务队列中,即使安排执行多次或者不限次数的,也仅仅执行一次。 +func (jl *Clock) StopGraceful() { + jl.exit() + + jl.immediate() +} diff --git a/dtask/cmd/cli_task b/dtask/cmd/cli_task new file mode 100644 index 0000000..a177f33 Binary files /dev/null and b/dtask/cmd/cli_task differ diff --git a/dtask/cmd/client/dtaskCli b/dtask/cmd/client/dtaskCli new file mode 100644 index 0000000..d56e937 Binary files /dev/null and b/dtask/cmd/client/dtaskCli differ diff --git a/dtask/cmd/client/main.go b/dtask/cmd/client/main.go new file mode 100644 index 0000000..d218a25 --- /dev/null +++ b/dtask/cmd/client/main.go @@ -0,0 +1,143 @@ +package main + +import ( + "bufio" + "encoding/json" + "flag" + "fmt" + "log" + "net/http" + "net/url" + "os" + "os/signal" + "strconv" + "syscall" + "time" + + "github.com/gorilla/websocket" + "github.com/oofpgDLD/dtask/proto" +) + +var addr = flag.String("addr", "172.16.101.107:17070", "http service address") + +var ( + closer = make(chan int, 1) + times = 5 + users = 10000 + + u = url.URL{Scheme: "ws", Host: *addr, Path: "/test"} +) + +func main() { + flag.Parse() + log.SetFlags(0) + + for i := 0; i < users; i++ { + go NewUser(i, times) + } + + go getInputByScanner() + // init signal + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) + for { + s := <-c + log.Printf("client get a signal %s", s.String()) + switch s { + case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT: + log.Print("client exit") + return + case syscall.SIGHUP: + // TODO reload + default: + return + } + } +} + +func NewUser(id int, times int) { + uid := fmt.Sprint(id) + log.Printf("connecting to %s, id %v", u.String(), fmt.Sprint(id)) + c, resp, err := websocket.DefaultDialer.Dial(u.String(), http.Header{ + "FZM-UID": {fmt.Sprint(id)}, + }) + if err != nil { + log.Fatal("dial:", err, resp, u.String()) + return + } + defer c.Close() + done := make(chan struct{}) + + go func() { + defer close(done) + for { + _, message, err := c.ReadMessage() + if err != nil { + log.Println("read:", err) + return + } + log.Printf("recv: %s", message) + } + }() + seq := 0 + ticker := time.NewTicker(time.Millisecond * 500) + defer ticker.Stop() + + for { + select { + case <-done: + return + case <-ticker.C: + seq++ + msg, err := Msg(uid, int64(seq)) + if err != nil { + log.Println("marshal proto:", err) + return + } + err = c.WriteMessage(websocket.TextMessage, msg) + if err != nil { + log.Println("write:", err) + return + } + if seq >= times { + ticker.Stop() + } + case index := <-closer: + log.Println("interrupt") + + if index == id { + log.Println("exit") + return + } + closer <- index + } + } +} + +func Msg(uid string, seq int64) ([]byte, error) { + p := &proto.Proto{ + Uid: uid, + Opt: proto.Start, + Seq: seq, + } + return json.Marshal(p) +} + +func getInputByScanner() string { + var str string + for { + //使用os.Stdin开启输入流 + in := bufio.NewScanner(os.Stdin) + if in.Scan() { + str = in.Text() + } else { + str = "Find input error" + } + + index, err := strconv.ParseInt(str, 10, 64) + if err != nil { + continue + } + closer <- int(index) + } +} diff --git a/dtask/cmd/dtaskSrv b/dtask/cmd/dtaskSrv new file mode 100644 index 0000000..1039688 Binary files /dev/null and b/dtask/cmd/dtaskSrv differ diff --git a/dtask/cmd/main.go b/dtask/cmd/main.go new file mode 100644 index 0000000..935a7de --- /dev/null +++ b/dtask/cmd/main.go @@ -0,0 +1,188 @@ +package main + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "log" + "net/http" + _ "net/http/pprof" + "os" + "os/signal" + "runtime/pprof" + "runtime/trace" + "syscall" + "time" + + gpprof "github.com/gin-contrib/pprof" + "github.com/gin-gonic/gin" + "github.com/gorilla/websocket" + "github.com/oofpgDLD/dtask" + "github.com/oofpgDLD/dtask/proto" +) + +const ( + _trace = false + _pprof = false + _pprof_net = true +) + +var addr = flag.String("addr", ":17070", "http service address") + +func main() { + if _trace { + f, err := os.Create("trace.out") + if err != nil { + panic(err) + } + defer f.Close() + + err = trace.Start(f) + if err != nil { + panic(err) + } + defer trace.Stop() + } + + if _pprof { + f, err := os.Create("pprof.out") + if err != nil { + panic(err) + } + err = pprof.StartCPUProfile(f) + if err != nil { + panic(err) + } + defer pprof.StopCPUProfile() + } + + r := gin.Default() + // websocket + r.GET("/test", ServeWs) + r.GET("/test2", func(context *gin.Context) { + context.String(http.StatusOK, "success") + }) + + srv := &http.Server{ + Addr: *addr, + Handler: r, + } + + if _pprof_net { + log.Printf("serve %s", *addr) + gpprof.Register(r) + } + go func() { + err := srv.ListenAndServe() + if err != nil { + log.Printf("http server stop %v", err) + } + }() + // init signal + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) + for { + s := <-c + log.Printf("service get a signal %s", s.String()) + switch s { + case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT: + log.Print("server exit") + return + case syscall.SIGHUP: + // TODO reload + default: + return + } + } +} + +var upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, + + // allow cross-origin + CheckOrigin: func(r *http.Request) bool { + return true + }, +} + +var ( + newline = []byte{'\n'} + space = []byte{' '} +) + +func ServeWs(context *gin.Context) { + var ( + ck *dtask.Clock + jobCache map[string]dtask.Job + wCh chan []byte + ) + // userId = context.GetHeader("FZM-UID") + c, err := upgrader.Upgrade(context.Writer, context.Request, nil) + if err != nil { + log.Print("upgrade:", err) + return + } + wCh = make(chan []byte) + defer c.Close() + defer onClose(ck) + done := make(chan struct{}) + ck = dtask.NewClock() + jobCache = make(map[string]dtask.Job) + + go func() { + defer close(done) + for { + _, message, err := c.ReadMessage() + if err != nil { + log.Println("read:", err) + return + } + log.Printf("recv: %s", message) + onRecvMsg(ck, jobCache, wCh, message) + } + }() + + for { + select { + case <-done: + return + case data := <-wCh: + err = c.WriteMessage(websocket.TextMessage, data) + if err != nil { + log.Println("write:", err) + break + } + } + } +} + +func onRecvMsg(ck *dtask.Clock, jc map[string]dtask.Job, wCh chan []byte, msg []byte) { + msg = bytes.TrimSpace(bytes.Replace(msg, newline, space, -1)) + p := proto.Proto{} + json.Unmarshal(msg, &p) + switch p.Opt { + case proto.Start: + job, inserted := ck.AddJobRepeat(time.Second*5, 0, func() { + wCh <- msg + }) + if !inserted { + break + } + jc[fmt.Sprint(p.Seq)] = job + case proto.Stop: + if job := jc[fmt.Sprint(p.Seq)]; job != nil { + job.Cancel() + } + default: + } +} + +func onClose(ck *dtask.Clock) { + if ck == nil { + log.Printf("onClose WsConnection args can not find") + return + } + ck.Stop() +} diff --git a/dtask/cmd/trace.out b/dtask/cmd/trace.out new file mode 100644 index 0000000..503720d Binary files /dev/null and b/dtask/cmd/trace.out differ diff --git a/dtask/debug.go b/dtask/debug.go new file mode 100644 index 0000000..153e088 --- /dev/null +++ b/dtask/debug.go @@ -0,0 +1,6 @@ +package dtask + +const ( + // Debug debug switch + Debug = false +) diff --git a/dtask/job.go b/dtask/job.go new file mode 100644 index 0000000..fe62ff6 --- /dev/null +++ b/dtask/job.go @@ -0,0 +1,99 @@ +package dtask + +import ( + "gitlab.33.cn/chat/im/dtask/pkg/rbtree" + "log" + "runtime/debug" + "sync/atomic" + "time" +) + +/* +This code is based on the following resources: +source code: https://github.com/alex023/clock.git +*/ +// Job External access interface for timed tasks +type Job interface { + C() <-chan Job //C Get a Chan,which can get message if Job is executed + Count() uint64 //计数器,表示已执行(或触发)的次数 + Max() uint64 //允许执行的最大次数 + Cancel() //撤销加载的任务,不再定时执行 + isAvailable() bool //return true,if job not action or not cancel +} + +// jobItem implementation of "Job" interface and "rbtree.Item" interface +type jobItem struct { + id uint64 //唯一键值,内部由管理器生成,以区分同一时刻的不同任务事件 + actionCount uint64 //计数器,表示已执行(或触发)的次数 + actionMax uint64 //允许执行的最大次数 + intervalTime time.Duration //间隔时间 + createTime time.Time //创建时间,略有误差 + actionTime time.Time //计算得出的最近一次执行时间点 + fn func() //事件函数 + msgChan chan Job //消息通道,执行时,控制器通过该通道向外部传递消息 + cancelFlag int32 + clock *Clock +} + +// Less Based rbtree ,implements Item interface for sort +func (je jobItem) Less(another rbtree.Item) bool { + item, ok := another.(*jobItem) + if !ok { + return false + } + if !je.actionTime.Equal(item.actionTime) { + return je.actionTime.Before(item.actionTime) + } + return je.id < item.id +} + +func (je *jobItem) C() <-chan Job { + return je.msgChan +} + +func (je *jobItem) action(async bool) { + je.actionCount++ + if async { + go safeCall(je.fn) + } else { + safeCall(je.fn) + } + select { + case je.msgChan <- je: + default: + //some times,client should not receive msgChan,so must discard jobItem when blocking + } +} + +func (je *jobItem) Cancel() { + if atomic.CompareAndSwapInt32(&je.cancelFlag, 0, 1) { + je.clock.rmJob(je) + je.innerCancel() + } +} +func (je *jobItem) isAvailable() bool { + return je.cancelFlag == 0 +} +func (je *jobItem) innerCancel() { + je.clock = nil + close(je.msgChan) +} + +// Count implement for Job +func (je jobItem) Count() uint64 { + return je.actionCount +} + +// Max implement for Job +func (je jobItem) Max() uint64 { + return je.actionMax +} +func safeCall(fn func()) { + defer func() { + if err := recover(); err != nil { + log.Printf("[clock] recovering reason is %+v. More detail:", err) + log.Println(string(debug.Stack())) + } + }() + fn() +} diff --git a/dtask/pkg/rbtree/LICENSE b/dtask/pkg/rbtree/LICENSE new file mode 100644 index 0000000..d2e29f7 --- /dev/null +++ b/dtask/pkg/rbtree/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2015, Hu Keping +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +(*) Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. + +(*) Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +(*) Neither the name of Hu Keping nor the names of its contributors may be +used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/dtask/pkg/rbtree/README.md b/dtask/pkg/rbtree/README.md new file mode 100644 index 0000000..44a2cd6 --- /dev/null +++ b/dtask/pkg/rbtree/README.md @@ -0,0 +1,143 @@ +# Rbtree [![GoDoc](https://godoc.org/github.com/HuKeping/rbtree?status.svg)](https://godoc.org/github.com/HuKeping/rbtree) + +This is an implementation of Red-Black tree written by Golang and does **not** support `duplicate keys`. + +## Installation + +With a healthy Go language installed, simply run `go get github.com/HuKeping/rbtree` + +## Example +All you have to do is to implement a comparison function `Less() bool` for your Item +which will be store in the Red-Black tree, here are some examples. +#### A simple case for `int` items. + package main + + import ( + "fmt" + "github.com/HuKeping/rbtree" + ) + + func main() { + rbt := rbtree.New() + + m := 0 + n := 10 + + for m < n { + rbt.Insert(rbtree.Int(m)) + m++ + } + + m = 0 + for m < n { + if m%2 == 0 { + rbt.Delete(rbtree.Int(m)) + } + m++ + } + + // 1, 3, 5, 7, 9 were expected. + rbt.Ascend(rbt.Min(), Print) + } + + func Print(item rbtree.Item) bool { + i, ok := item.(rbtree.Int) + if !ok { + return false + } + fmt.Println(i) + return true + } + +#### A simple case for `string` items. + package main + + import ( + "fmt" + "github.com/HuKeping/rbtree" + ) + + func main() { + rbt := rbtree.New() + + rbt.Insert(rbtree.String("Hello")) + rbt.Insert(rbtree.String("World")) + + rbt.Ascend(rbt.Min(), Print) + } + + func Print(item rbtree.Item) bool { + i, ok := item.(rbtree.String) + if !ok { + return false + } + fmt.Println(i) + return true + } + +#### A quite interesting case for `struct` items. + package main + + import ( + "fmt" + "github.com/HuKeping/rbtree" + "time" + ) + + type Var struct { + Expiry time.Time `json:"expiry,omitempty"` + ID string `json:"id",omitempty` + } + + // We will order the node by `Time` + func (x Var) Less(than rbtree.Item) bool { + return x.Expiry.Before(than.(Var).Expiry) + } + + func main() { + rbt := rbtree.New() + + var1 := Var{ + Expiry: time.Now().Add(time.Second * 10), + ID: "var1", + } + var2 := Var{ + Expiry: time.Now().Add(time.Second * 20), + ID: "var2", + } + var3 := Var{ + Expiry: var2.Expiry, + ID: "var2-dup", + } + var4 := Var{ + Expiry: time.Now().Add(time.Second * 40), + ID: "var4", + } + var5 := Var{ + Expiry: time.Now().Add(time.Second * 50), + ID: "var5", + } + + rbt.Insert(var1) + rbt.Insert(var2) + rbt.Insert(var3) + rbt.Insert(var4) + rbt.Insert(var5) + + tmp := Var{ + Expiry: var4.Expiry, + ID: "This field is not the key factor", + } + + // var4 and var5 were expected + rbt.Ascend(rbt.Get(tmp), Print) + } + + func Print(item rbtree.Item) bool { + i, ok := item.(Var) + if !ok { + return false + } + fmt.Printf("%+v\n", i) + return true + } diff --git a/dtask/pkg/rbtree/example/example_int/example_int.go b/dtask/pkg/rbtree/example/example_int/example_int.go new file mode 100644 index 0000000..ae4d008 --- /dev/null +++ b/dtask/pkg/rbtree/example/example_int/example_int.go @@ -0,0 +1,42 @@ +// Copyright 2015, Hu Keping. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + + "github.com/oofpgDLD/dtask/pkg/rbtree" +) + +func main() { + rbt := rbtree.New() + + m := 0 + n := 10 + + for m < n { + rbt.Insert(rbtree.Int(m)) + m++ + } + + m = 0 + for m < n { + if m%2 == 0 { + rbt.Delete(rbtree.Int(m)) + } + m++ + } + + rbt.Ascend(rbt.Min(), print) +} + +func print(item rbtree.Item) bool { + i, ok := item.(rbtree.Int) + if !ok { + return false + } + fmt.Println(i) + return true +} diff --git a/dtask/pkg/rbtree/example/example_string/example_string.go b/dtask/pkg/rbtree/example/example_string/example_string.go new file mode 100644 index 0000000..41e2f2a --- /dev/null +++ b/dtask/pkg/rbtree/example/example_string/example_string.go @@ -0,0 +1,29 @@ +// Copyright 2015, Hu Keping. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + + "github.com/oofpgDLD/dtask/pkg/rbtree" +) + +func main() { + rbt := rbtree.New() + + rbt.Insert(rbtree.String("Hello")) + rbt.Insert(rbtree.String("World")) + + rbt.Ascend(rbt.Min(), print) +} + +func print(item rbtree.Item) bool { + i, ok := item.(rbtree.String) + if !ok { + return false + } + fmt.Println(i) + return true +} diff --git a/dtask/pkg/rbtree/example/example_struct/example_struct.go b/dtask/pkg/rbtree/example/example_struct/example_struct.go new file mode 100644 index 0000000..3e38a37 --- /dev/null +++ b/dtask/pkg/rbtree/example/example_struct/example_struct.go @@ -0,0 +1,71 @@ +// Copyright 2015, Hu Keping. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "time" + + "github.com/oofpgDLD/dtask/pkg/rbtree" +) + +// Var is the node of a struct +type Var struct { + Expiry time.Time `json:"expiry,omitempty"` + ID string `json:"id,omitempty"` +} + +// Less will order the node by `Time` +func (x Var) Less(than rbtree.Item) bool { + return x.Expiry.Before(than.(Var).Expiry) +} + +func main() { + rbt := rbtree.New() + + var1 := Var{ + Expiry: time.Now().Add(time.Second * 10), + ID: "var1", + } + var2 := Var{ + Expiry: time.Now().Add(time.Second * 20), + ID: "var2", + } + var3 := Var{ + Expiry: var2.Expiry, + ID: "var2-dup", + } + var4 := Var{ + Expiry: time.Now().Add(time.Second * 40), + ID: "var4", + } + var5 := Var{ + Expiry: time.Now().Add(time.Second * 50), + ID: "var5", + } + + rbt.Insert(var1) + rbt.Insert(var2) + rbt.Insert(var3) + rbt.Insert(var4) + rbt.Insert(var5) + + tmp := Var{ + Expiry: var4.Expiry, + ID: "This field is not the key factor", + } + + // var4 and var5 were expected + rbt.Ascend(rbt.Get(tmp), print) +} + +func print(item rbtree.Item) bool { + i, ok := item.(Var) + if !ok { + return false + } + fmt.Printf("%+v\n", i) + return true +} diff --git a/dtask/pkg/rbtree/iterator.go b/dtask/pkg/rbtree/iterator.go new file mode 100644 index 0000000..a4d77e6 --- /dev/null +++ b/dtask/pkg/rbtree/iterator.go @@ -0,0 +1,93 @@ +// Copyright 2015, Hu Keping. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rbtree + +// Iterator is the function of iteration entity which would be +// used by those functions like `Ascend`, `Dscend`, etc. +// +// A typical Iterator with Print : +// func loop_with_print(item rbtree.Item) bool { +// i, ok := item.(XXX) +// if !ok { +// return false +// } +// fmt.Printf("%+v\n", i) +// return true +// } +type Iterator func(i Item) bool + +// Ascend will call iterator once for each element greater or equal than pivot +// in ascending order. It will stop whenever the iterator returns false. +func (t *Rbtree) Ascend(pivot Item, iterator Iterator) { + t.ascend(t.root, pivot, iterator) +} + +func (t *Rbtree) ascend(x *Node, pivot Item, iterator Iterator) bool { + if x == t.NIL { + return true + } + + if !less(x.Item, pivot) { + if !t.ascend(x.Left, pivot, iterator) { + return false + } + if !iterator(x.Item) { + return false + } + } + + return t.ascend(x.Right, pivot, iterator) +} + +// Descend will call iterator once for each element less or equal than pivot +// in descending order. It will stop whenever the iterator returns false. +func (t *Rbtree) Descend(pivot Item, iterator Iterator) { + t.descend(t.root, pivot, iterator) +} + +func (t *Rbtree) descend(x *Node, pivot Item, iterator Iterator) bool { + if x == t.NIL { + return true + } + + if !less(pivot, x.Item) { + if !t.descend(x.Right, pivot, iterator) { + return false + } + if !iterator(x.Item) { + return false + } + } + + return t.descend(x.Left, pivot, iterator) +} + +// AscendRange will call iterator once for elements greater or equal than @ge +// and less than @lt, which means the range would be [ge, lt). +// It will stop whenever the iterator returns false. +func (t *Rbtree) AscendRange(ge, lt Item, iterator Iterator) { + t.ascendRange(t.root, ge, lt, iterator) +} + +func (t *Rbtree) ascendRange(x *Node, inf, sup Item, iterator Iterator) bool { + if x == t.NIL { + return true + } + + if !less(x.Item, sup) { + return t.ascendRange(x.Left, inf, sup, iterator) + } + if less(x.Item, inf) { + return t.ascendRange(x.Right, inf, sup, iterator) + } + + if !t.ascendRange(x.Left, inf, sup, iterator) { + return false + } + if !iterator(x.Item) { + return false + } + return t.ascendRange(x.Right, inf, sup, iterator) +} diff --git a/dtask/pkg/rbtree/rbtree.go b/dtask/pkg/rbtree/rbtree.go new file mode 100644 index 0000000..bc77a38 --- /dev/null +++ b/dtask/pkg/rbtree/rbtree.go @@ -0,0 +1,426 @@ +// Copyright 2015, Hu Keping . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package rbtree implements operations on Red-Black tree. +package rbtree + +// +// Red-Black tree properties: http://en.wikipedia.org/wiki/Rbtree +// +// 1) A node is either red or black +// 2) The root is black +// 3) All leaves (NULL) are black +// 4) Both children of every red node are black +// 5) Every simple path from root to leaves contains the same number +// of black nodes. +// + +// Node of the rbtree has a pointer of the node of parent, left, right, also has own color and Item which client uses +type Node struct { + Left *Node + Right *Node + Parent *Node + Color uint + + // for use by client. + Item +} + +const ( + // RED represents the color of the node is red + RED = 0 + // BLACK represents the color of the node is black + BLACK = 1 +) + +// Item has a method to compare items which is less +type Item interface { + Less(than Item) bool +} + +// Rbtree represents a Red-Black tree. +type Rbtree struct { + NIL *Node + root *Node + count uint +} + +func less(x, y Item) bool { + return x.Less(y) +} + +// New returns an initialized Red-Black tree +func New() *Rbtree { return new(Rbtree).Init() } + +// Init returns the initial of rbtree +func (t *Rbtree) Init() *Rbtree { + node := &Node{nil, nil, nil, BLACK, nil} + return &Rbtree{ + NIL: node, + root: node, + count: 0, + } +} + +func (t *Rbtree) leftRotate(x *Node) { + // Since we are doing the left rotation, the right child should *NOT* nil. + if x.Right == t.NIL { + return + } + + // + // The illation of left rotation + // + // | | + // X Y + // / \ left rotate / \ + // α Y -------------> X γ + // / \ / \ + // β γ α β + // + // It should be note that during the rotating we do not change + // the Nodes' color. + // + y := x.Right + x.Right = y.Left + if y.Left != t.NIL { + y.Left.Parent = x + } + y.Parent = x.Parent + + if x.Parent == t.NIL { + t.root = y + } else if x == x.Parent.Left { + x.Parent.Left = y + } else { + x.Parent.Right = y + } + + y.Left = x + x.Parent = y +} + +func (t *Rbtree) rightRotate(x *Node) { + // Since we are doing the right rotation, the left child should *NOT* nil. + if x.Left == t.NIL { + return + } + + // + // The illation of right rotation + // + // | | + // X Y + // / \ right rotate / \ + // Y γ -------------> α X + // / \ / \ + // α β β γ + // + // It should be note that during the rotating we do not change + // the Nodes' color. + // + y := x.Left + x.Left = y.Right + if y.Right != t.NIL { + y.Right.Parent = x + } + y.Parent = x.Parent + + if x.Parent == t.NIL { + t.root = y + } else if x == x.Parent.Left { + x.Parent.Left = y + } else { + x.Parent.Right = y + } + + y.Right = x + x.Parent = y +} + +func (t *Rbtree) insert(z *Node) *Node { + x := t.root + y := t.NIL + + for x != t.NIL { + y = x + if less(z.Item, x.Item) { + x = x.Left + } else if less(x.Item, z.Item) { + x = x.Right + } else { + return x + } + } + + z.Parent = y + if y == t.NIL { + t.root = z + } else if less(z.Item, y.Item) { + y.Left = z + } else { + y.Right = z + } + + t.count++ + t.insertFixup(z) + return z +} + +func (t *Rbtree) insertFixup(z *Node) { + for z.Parent.Color == RED { + // + // Howerver, we do not need the assertion of non-nil grandparent + // because + // + // 2) The root is black + // + // Since the color of the parent is RED, so the parent is not root + // and the grandparent must be exist. + // + if z.Parent == z.Parent.Parent.Left { + // Take y as the uncle, although it can be NIL, in that case + // its color is BLACK + y := z.Parent.Parent.Right + if y.Color == RED { + // + // Case 1: + // Parent and uncle are both RED, the grandparent must be BLACK + // due to + // + // 4) Both children of every red node are black + // + // Since the current node and its parent are all RED, we still + // in violation of 4), So repaint both the parent and the uncle + // to BLACK and grandparent to RED(to maintain 5) + // + // 5) Every simple path from root to leaves contains the same + // number of black nodes. + // + z.Parent.Color = BLACK + y.Color = BLACK + z.Parent.Parent.Color = RED + z = z.Parent.Parent + } else { + if z == z.Parent.Right { + // + // Case 2: + // Parent is RED and uncle is BLACK and the current node + // is right child + // + // A left rotation on the parent of the current node will + // switch the roles of each other. This still leaves us in + // violation of 4). + // The continuation into Case 3 will fix that. + // + z = z.Parent + t.leftRotate(z) + } + // + // Case 3: + // Parent is RED and uncle is BLACK and the current node is + // left child + // + // At the very beginning of Case 3, current node and parent are + // both RED, thus we violate 4). + // Repaint parent to BLACK will fix it, but 5) does not allow + // this because all paths that go through the parent will get + // 1 more black node. Then repaint grandparent to RED (as we + // discussed before, the grandparent is BLACK) and do a right + // rotation will fix that. + // + z.Parent.Color = BLACK + z.Parent.Parent.Color = RED + t.rightRotate(z.Parent.Parent) + } + } else { // same as then clause with "right" and "left" exchanged + y := z.Parent.Parent.Left + if y.Color == RED { + z.Parent.Color = BLACK + y.Color = BLACK + z.Parent.Parent.Color = RED + z = z.Parent.Parent + } else { + if z == z.Parent.Left { + z = z.Parent + t.rightRotate(z) + } + z.Parent.Color = BLACK + z.Parent.Parent.Color = RED + t.leftRotate(z.Parent.Parent) + } + } + } + t.root.Color = BLACK +} + +// Just traverse the node from root to left recursively until left is NIL. +// The node whose left is NIL is the node with minimum value. +func (t *Rbtree) min(x *Node) *Node { + if x == t.NIL { + return t.NIL + } + + for x.Left != t.NIL { + x = x.Left + } + + return x +} + +// Just traverse the node from root to right recursively until right is NIL. +// The node whose right is NIL is the node with maximum value. +func (t *Rbtree) max(x *Node) *Node { + if x == t.NIL { + return t.NIL + } + + for x.Right != t.NIL { + x = x.Right + } + + return x +} + +func (t *Rbtree) search(x *Node) *Node { + p := t.root + + for p != t.NIL { + if less(p.Item, x.Item) { + p = p.Right + } else if less(x.Item, p.Item) { + p = p.Left + } else { + break + } + } + + return p +} + +//TODO: Need Document +func (t *Rbtree) successor(x *Node) *Node { + if x == t.NIL { + return t.NIL + } + + // Get the minimum from the right sub-tree if it existed. + if x.Right != t.NIL { + return t.min(x.Right) + } + + y := x.Parent + for y != t.NIL && x == y.Right { + x = y + y = y.Parent + } + return y +} + +//TODO: Need Document +func (t *Rbtree) delete(key *Node) *Node { + z := t.search(key) + + if z == t.NIL { + return t.NIL + } + ret := &Node{t.NIL, t.NIL, t.NIL, z.Color, z.Item} + + var y *Node + var x *Node + + if z.Left == t.NIL || z.Right == t.NIL { + y = z + } else { + y = t.successor(z) + } + + if y.Left != t.NIL { + x = y.Left + } else { + x = y.Right + } + + // Even if x is NIL, we do the assign. In that case all the NIL nodes will + // change from {nil, nil, nil, BLACK, nil} to {nil, nil, ADDR, BLACK, nil}, + // but do not worry about that because it will not affect the compare + // between Node-X with Node-NIL + x.Parent = y.Parent + + if y.Parent == t.NIL { + t.root = x + } else if y == y.Parent.Left { + y.Parent.Left = x + } else { + y.Parent.Right = x + } + + if y != z { + z.Item = y.Item + } + + if y.Color == BLACK { + t.deleteFixup(x) + } + + t.count-- + + return ret +} + +func (t *Rbtree) deleteFixup(x *Node) { + for x != t.root && x.Color == BLACK { + if x == x.Parent.Left { + w := x.Parent.Right + if w.Color == RED { + w.Color = BLACK + x.Parent.Color = RED + t.leftRotate(x.Parent) + w = x.Parent.Right + } + if w.Left.Color == BLACK && w.Right.Color == BLACK { + w.Color = RED + x = x.Parent + } else { + if w.Right.Color == BLACK { + w.Left.Color = BLACK + w.Color = RED + t.rightRotate(w) + w = x.Parent.Right + } + w.Color = x.Parent.Color + x.Parent.Color = BLACK + w.Right.Color = BLACK + t.leftRotate(x.Parent) + // this is to exit while loop + x = t.root + } + } else { // the code below is has left and right switched from above + w := x.Parent.Left + if w.Color == RED { + w.Color = BLACK + x.Parent.Color = RED + t.rightRotate(x.Parent) + w = x.Parent.Left + } + if w.Left.Color == BLACK && w.Right.Color == BLACK { + w.Color = RED + x = x.Parent + } else { + if w.Left.Color == BLACK { + w.Right.Color = BLACK + w.Color = RED + t.leftRotate(w) + w = x.Parent.Left + } + w.Color = x.Parent.Color + x.Parent.Color = BLACK + w.Left.Color = BLACK + t.rightRotate(x.Parent) + x = t.root + } + } + } + x.Color = BLACK +} diff --git a/dtask/pkg/rbtree/rbtree_test.go b/dtask/pkg/rbtree/rbtree_test.go new file mode 100644 index 0000000..36c1084 --- /dev/null +++ b/dtask/pkg/rbtree/rbtree_test.go @@ -0,0 +1,200 @@ +// Copyright 2015, Hu Keping. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rbtree + +import ( + "reflect" + "testing" +) + +func TestInsertAndDelete(t *testing.T) { + rbt := New() + + m := 0 + n := 1000 + for m < n { + rbt.Insert(Int(m)) + m++ + } + if rbt.Len() != uint(n) { + t.Errorf("tree.Len() = %d, expect %d", rbt.Len(), n) + } + + for m > 0 { + rbt.Delete(Int(m)) + m-- + } + if rbt.Len() != 1 { + t.Errorf("tree.Len() = %d, expect %d", rbt.Len(), 1) + } +} + +type testStruct struct { + id int + text string +} + +func (ts *testStruct) Less(than Item) bool { + return ts.id < than.(*testStruct).id +} + +func TestInsertOrGet(t *testing.T) { + rbt := New() + + items := []*testStruct{ + {1, "this"}, + {2, "is"}, + {3, "a"}, + {4, "test"}, + } + + for i := range items { + rbt.Insert(items[i]) + } + + newItem := &testStruct{items[0].id, "not"} + newItem = rbt.InsertOrGet(newItem).(*testStruct) + + if newItem.text != items[0].text { + t.Errorf("tree.InsertOrGet = {id: %d, text: %s}, expect {id %d, text %s}", newItem.id, newItem.text, items[0].id, items[0].text) + } + + newItem = &testStruct{5, "new"} + newItem = rbt.InsertOrGet(newItem).(*testStruct) + + if newItem.text != "new" { + t.Errorf("tree.InsertOrGet = {id: %d, text: %s}, expect {id %d, text %s}", newItem.id, newItem.text, 5, "new") + } +} + +func TestInsertString(t *testing.T) { + rbt := New() + + rbt.Insert(String("go")) + rbt.Insert(String("lang")) + + if rbt.Len() != 2 { + t.Errorf("tree.Len() = %d, expect %d", rbt.Len(), 2) + } +} + +// Test for duplicate +func TestInsertDup(t *testing.T) { + rbt := New() + + rbt.Insert(String("go")) + rbt.Insert(String("go")) + rbt.Insert(String("go")) + + if rbt.Len() != 1 { + t.Errorf("tree.Len() = %d, expect %d", rbt.Len(), 1) + } +} + +func TestDescend(t *testing.T) { + rbt := New() + + m := 0 + n := 10 + for m < n { + rbt.Insert(Int(m)) + m++ + } + + var ret []Item + + rbt.Descend(Int(1), func(i Item) bool { + ret = append(ret, i) + return true + }) + expected := []Item{Int(1), Int(0)} + if !reflect.DeepEqual(ret, expected) { + t.Errorf("expected %v but got %v", expected, ret) + } + + ret = nil + rbt.Descend(Int(10), func(i Item) bool { + ret = append(ret, i) + return true + }) + expected = []Item{Int(9), Int(8), Int(7), Int(6), Int(5), Int(4), Int(3), Int(2), Int(1), Int(0)} + if !reflect.DeepEqual(ret, expected) { + t.Errorf("expected %v but got %v", expected, ret) + } +} + +func TestGet(t *testing.T) { + rbt := New() + + rbt.Insert(Int(1)) + rbt.Insert(Int(2)) + rbt.Insert(Int(3)) + + no := rbt.Get(Int(100)) + ok := rbt.Get(Int(1)) + + if no != nil { + t.Errorf("100 is expect not exists") + } + + if ok == nil { + t.Errorf("1 is expect exists") + } +} + +func TestAscend(t *testing.T) { + rbt := New() + + rbt.Insert(String("a")) + rbt.Insert(String("b")) + rbt.Insert(String("c")) + rbt.Insert(String("d")) + + rbt.Delete(rbt.Min()) + + var ret []Item + rbt.Ascend(rbt.Min(), func(i Item) bool { + ret = append(ret, i) + return true + }) + + expected := []Item{String("b"), String("c"), String("d")} + if !reflect.DeepEqual(ret, expected) { + t.Errorf("expected %v but got %v", expected, ret) + } +} + +func TestMax(t *testing.T) { + rbt := New() + + rbt.Insert(String("z")) + rbt.Insert(String("h")) + rbt.Insert(String("a")) + + expected := String("z") + if rbt.Max() != expected { + t.Errorf("expected Max of tree as %v but got %v", expected, rbt.Max()) + } +} + +func TestAscendRange(t *testing.T) { + rbt := New() + + strings := []String{"a", "b", "c", "aa", "ab", "ac", "abc", "acb", "bac"} + for _, v := range strings { + rbt.Insert(v) + } + + var ret []Item + rbt.AscendRange(String("ab"), String("b"), func(i Item) bool { + ret = append(ret, i) + return true + }) + expected := []Item{String("ab"), String("abc"), String("ac"), String("acb")} + + if !reflect.DeepEqual(ret, expected) { + t.Errorf("expected %v but got %v", expected, ret) + } +} diff --git a/dtask/pkg/rbtree/stats.go b/dtask/pkg/rbtree/stats.go new file mode 100644 index 0000000..64f9d66 --- /dev/null +++ b/dtask/pkg/rbtree/stats.go @@ -0,0 +1,88 @@ +// Copyright 2015, Hu Keping . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rbtree + +// This file contains most of the methods that can be used +// by the user. Anyone who wants to look for some API about +// the rbtree, this is the right place. + +// Len returns number of nodes in the tree. +func (t *Rbtree) Len() uint { return t.count } + +// Insert func inserts a item as a new RED node +func (t *Rbtree) Insert(item Item) { + if item == nil { + return + } + + // Always insert a RED node + t.insert(&Node{t.NIL, t.NIL, t.NIL, RED, item}) +} + +//InsertOrGet inserts or retrieves the item in the tree. If the +//item is already in the tree then the return value will be that. +//If the item is not in the tree the return value will be the item +//you put in. +func (t *Rbtree) InsertOrGet(item Item) Item { + if item == nil { + return nil + } + + return t.insert(&Node{t.NIL, t.NIL, t.NIL, RED, item}).Item +} + +//Delete delete the item in the tree +func (t *Rbtree) Delete(item Item) Item { + if item == nil { + return nil + } + + // The `color` field here is nobody + return t.delete(&Node{t.NIL, t.NIL, t.NIL, RED, item}).Item +} + +//Get search for the specified items which is carried by a Node +func (t *Rbtree) Get(item Item) Item { + if item == nil { + return nil + } + + // The `color` field here is nobody + ret := t.search(&Node{t.NIL, t.NIL, t.NIL, RED, item}) + if ret == nil { + return nil + } + + return ret.Item +} + +// Search does only search the node which includes it node +//TODO: This is for debug, delete it in the future +func (t *Rbtree) Search(item Item) *Node { + + return t.search(&Node{t.NIL, t.NIL, t.NIL, RED, item}) +} + +// Min return the item minimum one +func (t *Rbtree) Min() Item { + x := t.min(t.root) + + if x == t.NIL { + return nil + } + + return x.Item +} + +// Max return the item maxmum one +func (t *Rbtree) Max() Item { + x := t.max(t.root) + + if x == t.NIL { + return nil + } + + return x.Item +} diff --git a/dtask/pkg/rbtree/stats_test.go b/dtask/pkg/rbtree/stats_test.go new file mode 100644 index 0000000..a56a794 --- /dev/null +++ b/dtask/pkg/rbtree/stats_test.go @@ -0,0 +1,152 @@ +// Copyright 2015, Hu Keping. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rbtree + +import ( + "fmt" + "reflect" + "sync" + "testing" + "time" +) + +func TestDeleteReturnValue(t *testing.T) { + rbt := New() + + rbt.Insert(String("go")) + rbt.Insert(String("lang")) + + if rbt.Len() != 2 { + t.Errorf("tree.Len() = %d, expect %d", rbt.Len(), 2) + } + + // go should be in the rbtree + deletedGo := rbt.Delete(String("go")) + if deletedGo != String("go") { + t.Errorf("expect %v, got %v", "go", deletedGo) + } + + // C should not be in the rbtree + deletedC := rbt.Delete(String("C")) + if deletedC != nil { + t.Errorf("expect %v, got %v", nil, deletedC) + } +} + +func TestMin(t *testing.T) { + rbt := New() + + m := 0 + n := 1000 + for m < n { + rbt.Insert(Int(m)) + m++ + } + if rbt.Len() != uint(n) { + t.Errorf("tree.Len() = %d, expect %d", rbt.Len(), n) + } + + for m >= 0 { + rbt.Delete(rbt.Min()) + m-- + } + + if rbt.Len() != 0 { + t.Errorf("tree.Len() = %d, expect %d", rbt.Len(), 0) + } +} + +// +// This test will first add 1000 numbers into a tree and then delete some +// from it. +// +// All the adding and deleting are in goroutine so that the delete action +// will not always succeed for example `delete(400)` but `add(400)` has not +// been executed yet. +// +// Finally, we'll add back all the deleted nodes to see if there is +// anything wrong. +// +func TestWithGoroutine(t *testing.T) { + var Cache struct { + rbt *Rbtree + locker *sync.Mutex + } + + var DeletedArray struct { + array []Item + locker *sync.Mutex + } + + // Init the rbtree and the locker for later use + Cache.rbt = New() + Cache.locker = new(sync.Mutex) + + // Init the locker for later use + DeletedArray.locker = new(sync.Mutex) + + i, m := 0, 0 + j, n := 1000, 1000 + + var expected []Item + + // This loop will add intergers [m~n) to the rbtree. + for m < n { + expected = append(expected, Int(m)) + + go func(x Int) { + Cache.locker.Lock() + Cache.rbt.Insert(x) + Cache.locker.Unlock() + }(Int(m)) + m++ + } + + // This loop will try to delete the even integers in [m~n), + // Be noticed that the delete will not always succeeds since we are + // in the goroutines. + // We will record which ones have been removed. + for i < j { + if i%2 == 0 { + go func(x Int) { + Cache.locker.Lock() + value := Cache.rbt.Delete(x) + Cache.locker.Unlock() + + DeletedArray.locker.Lock() + DeletedArray.array = append(DeletedArray.array, value) + DeletedArray.locker.Unlock() + }(Int(i)) + } + i++ + } + + // Let's give a little time to those goroutines to finish their job. + time.Sleep(time.Second * 1) + + // Add deleted Items back + cnt := 0 + DeletedArray.locker.Lock() + for _, v := range DeletedArray.array { + if v != nil { + Cache.locker.Lock() + Cache.rbt.Insert(v) + Cache.locker.Unlock() + cnt++ + } + } + DeletedArray.locker.Unlock() + fmt.Printf("In TestWithGoroutine(), we have deleted [%v] nodes.\n", cnt) + + var ret []Item + Cache.rbt.Ascend(Cache.rbt.Min(), func(item Item) bool { + ret = append(ret, item) + return true + }) + + if !reflect.DeepEqual(ret, expected) { + t.Errorf("expected %v but got %v", expected, ret) + } +} diff --git a/dtask/pkg/rbtree/util.go b/dtask/pkg/rbtree/util.go new file mode 100644 index 0000000..dcb2e36 --- /dev/null +++ b/dtask/pkg/rbtree/util.go @@ -0,0 +1,21 @@ +// Copyright 2015, Hu Keping. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rbtree + +// Int is type of int +type Int int + +// Less returns whether x(Int) is smaller than specified item +func (x Int) Less(than Item) bool { + return x < than.(Int) +} + +// String is type of string +type String string + +// Less returns whether x(String) is smaller than specified item +func (x String) Less(than Item) bool { + return x < than.(String) +} diff --git a/dtask/proto/proto.go b/dtask/proto/proto.go new file mode 100644 index 0000000..15cbe84 --- /dev/null +++ b/dtask/proto/proto.go @@ -0,0 +1,12 @@ +package proto + +const ( + Start = 1 + Stop = 2 +) + +type Proto struct { + Uid string + Opt int + Seq int64 +} diff --git a/dtask/safetimer.go b/dtask/safetimer.go new file mode 100644 index 0000000..5a4a712 --- /dev/null +++ b/dtask/safetimer.go @@ -0,0 +1,33 @@ +package dtask + +import "time" + +/* +This code is based on the following resources: +source code: https://play.golang.org/p/Ys9qqanqmU +discuss: https://groups.google.com/forum/#!msg/golang-dev/c9UUfASVPoU/tlbK2BpFEwAJ +*/ +type safetimer struct { + *time.Timer + scr bool +} + +//saw channel read, must be called after receiving value from safetimer chan +func (t *safetimer) SCR() { + t.scr = true +} + +func (t *safetimer) SafeReset(d time.Duration) bool { + ret := t.Stop() + if !ret && !t.scr { + <-t.C + } + t.Timer.Reset(d) + t.scr = false + return ret +} +func newSafeTimer(d time.Duration) *safetimer { + return &safetimer{ + Timer: time.NewTimer(d), + } +} diff --git a/dtask/script/pprof.sh b/dtask/script/pprof.sh new file mode 100644 index 0000000..4d35cc4 --- /dev/null +++ b/dtask/script/pprof.sh @@ -0,0 +1,71 @@ +#!/bin/bash +default_url="http://172.16.101.107:16060" +default_prefix="debug/pprof" +default_type="profile" + +read -p "input the request url (default url is '${default_url}'): " url +if [ -z ${url} ]; then + url=${default_url} +fi +read -p "input the pprof prefix (default prefix is '${default_prefix}'): " prefix +if [ -z ${prefix} ]; then + prefix=${default_prefix} +fi + +function show() { + echo "chose the pprof type:" + echo " 0: break the loop" + echo " 1: profile" + echo " 2: heap" + echo " 3: allocs" + echo " 4: goroutine" + echo " 5: mutex" + echo " 6: block" + echo " 7: trace" + echo " 8: threadcreate" + echo " 9: cmdline" + echo -n "input the number: " +} + +while true; do + show + read num + case $num in + 0) + break + ;; + 1) + type="profile" + ;; + 2) + type="heap" + ;; + 3) + type="allocs" + ;; + 4) + type="goroutine" + ;; + 5) + type="mutex" + ;; + 6) + type="block" + ;; + 7) + type="trace" + ;; + 8) + type="threadcreate" + ;; + 9) + type="cmdline" + ;; + *) + type=${default_type} + ;; + esac + # echo 'file://wsl$/Ubuntu-18.04/tmp/' | clip.exe + # echo "request: ${url}/${prefix}/${type}" + go tool pprof "${url}/${prefix}/${type}" +done diff --git a/dtask/script/torch.svg b/dtask/script/torch.svg new file mode 100644 index 0000000..d42f583 --- /dev/null +++ b/dtask/script/torch.svg @@ -0,0 +1,1308 @@ + + + + + + + + + + + + + + +Flame Graph + +Reset Zoom +Search +ic + + + +runtime.futex (3 samples, 0.84%) + + + +runtime.gcDrainN (16 samples, 4.47%) +runti.. + + +runtime.(*guintptr).cas (2 samples, 0.56%) + + + +runtime.runtimer (1 samples, 0.28%) + + + +runtime.notesleep (1 samples, 0.28%) + + + +runtime.(*mcentral).cacheSpan (1 samples, 0.28%) + + + +runtime.scanobject (15 samples, 4.19%) +runt.. + + +runtime.gcAssistAlloc (16 samples, 4.47%) +runti.. + + +runtime.futexsleep (1 samples, 0.28%) + + + +runtime.dodeltimer0 (5 samples, 1.40%) + + + +sync.(*RWMutex).Unlock (1 samples, 0.28%) + + + +main.main.func1 (2 samples, 0.56%) + + + +runtime.nanotime (2 samples, 0.56%) + + + +runtime.(*mcache).prepareForSweep (1 samples, 0.28%) + + + +runtime.findrunnable (111 samples, 31.01%) +runtime.findrunnable + + +runtime.unlock2 (1 samples, 0.28%) + + + +runtime.write1 (4 samples, 1.12%) + + + +runtime.puintptr.ptr (1 samples, 0.28%) + + + +runtime.heapBits.bits (1 samples, 0.28%) + + + +runtime.goready.func1 (15 samples, 4.19%) +runt.. + + +runtime.(*mcache).prepareForSweep (1 samples, 0.28%) + + + +runtime.osyield (1 samples, 0.28%) + + + +runtime.mallocgc (16 samples, 4.47%) +runti.. + + +runtime.spanOf (1 samples, 0.28%) + + + +runtime.(*mcentral).cacheSpan (3 samples, 0.84%) + + + +runtime.newobject (1 samples, 0.28%) + + + +runtime.lockWithRank (3 samples, 0.84%) + + + +runtime.nanotime1 (1 samples, 0.28%) + + + +runtime.netpoll (13 samples, 3.63%) +runt.. + + +runtime.(*mcache).refill (2 samples, 0.56%) + + + +runtime.(*mheap).alloc (2 samples, 0.56%) + + + +runtime.schedule (1 samples, 0.28%) + + + +runtime.isEmpty (1 samples, 0.28%) + + + +runtime.findObject (1 samples, 0.28%) + + + +runtime.usleep (1 samples, 0.28%) + + + +runtime.gopark (3 samples, 0.84%) + + + +time.sendTime (7 samples, 1.96%) +t.. + + +runtime.send (1 samples, 0.28%) + + + +runtime.growWork_faststr (16 samples, 4.47%) +runti.. + + +runtime.goschedImpl (1 samples, 0.28%) + + + +runtime.pageIndexOf (2 samples, 0.56%) + + + +runtime.resettimer (3 samples, 0.84%) + + + +time.NewTicker (2 samples, 0.56%) + + + +runtime.notesleep (4 samples, 1.12%) + + + +time.Sleep (1 samples, 0.28%) + + + +runtime.mapassign_faststr (41 samples, 11.45%) +runtime.mapassign.. + + +runtime.timeSleepUntil (1 samples, 0.28%) + + + +runtime.newobject (2 samples, 0.56%) + + + +runtime.runOneTimer (53 samples, 14.80%) +runtime.runOneTimer + + +runtime.nanotime (2 samples, 0.56%) + + + +runtime.lock2 (1 samples, 0.28%) + + + +time.Now (1 samples, 0.28%) + + + +github.com/oofpgDLD/dtask.(*TaskPool).Add (2 samples, 0.56%) + + + +runtime.(*mcache).refill (3 samples, 0.84%) + + + +runtime.unlock2 (1 samples, 0.28%) + + + +github.com/oofpgDLD/dtask.(*Task).start (27 samples, 7.54%) +github.com.. + + +runtime.notetsleep (3 samples, 0.84%) + + + +memeqbody (1 samples, 0.28%) + + + +runtime.siftdownTimer (13 samples, 3.63%) +runt.. + + +runtime.spanOf (2 samples, 0.56%) + + + +runtime.memclrNoHeapPointers (2 samples, 0.56%) + + + +runtime.lockWithRank (1 samples, 0.28%) + + + +runtime.mallocgc (3 samples, 0.84%) + + + +runtime.findObject (2 samples, 0.56%) + + + +runtime.unlock2 (2 samples, 0.56%) + + + +time.sendTime (1 samples, 0.28%) + + + +runtime.scanstack.func1 (2 samples, 0.56%) + + + +runtime.runqgrab (1 samples, 0.28%) + + + +runtime.templateThread (4 samples, 1.12%) + + + +runtime.(*hmap).newoverflow (1 samples, 0.28%) + + + +runtime.memmove (1 samples, 0.28%) + + + +runtime.lockWithRank (1 samples, 0.28%) + + + +runtime.runOneTimer (6 samples, 1.68%) + + + +runtime.wakep (9 samples, 2.51%) +ru.. + + +runtime.lock2 (4 samples, 1.12%) + + + +runtime.futexwakeup (39 samples, 10.89%) +runtime.futexwak.. + + +runtime.notetsleep_internal (3 samples, 0.84%) + + + +runtime.resetspinning (39 samples, 10.89%) +runtime.resetspi.. + + +runtime.wakep (39 samples, 10.89%) +runtime.wakep + + +runtime.walltime (3 samples, 0.84%) + + + +github.com/oofpgDLD/dtask.(*Task).Add (50 samples, 13.97%) +github.com/oofpgDLD/d.. + + +runtime.(*mheap).alloc.func1 (1 samples, 0.28%) + + + +runtime.(*mheap).allocSpan (1 samples, 0.28%) + + + +runtime.(*mcentral).grow (2 samples, 0.56%) + + + +runtime.selectnbsend (2 samples, 0.56%) + + + +main.main.func1.1.1.1 (20 samples, 5.59%) +main.ma.. + + +runtime.scanstack (2 samples, 0.56%) + + + +runtime.(*mcache).nextFree (1 samples, 0.28%) + + + +runtime.runtimer (8 samples, 2.23%) +r.. + + +runtime.add (1 samples, 0.28%) + + + +runtime.mallocgc (2 samples, 0.56%) + + + +runtime.netpoll (1 samples, 0.28%) + + + +runtime.markroot.func1 (2 samples, 0.56%) + + + +runtime.lockWithRank (4 samples, 1.12%) + + + +runtime.lock2 (1 samples, 0.28%) + + + +runtime.add1 (2 samples, 0.56%) + + + +runtime.typedmemmove (2 samples, 0.56%) + + + +runtime.arenaIndex (1 samples, 0.28%) + + + +runtime.checkTimers (64 samples, 17.88%) +runtime.checkTimers + + +runtime.futex (17 samples, 4.75%) +runti.. + + +runtime.dodeltimer0 (14 samples, 3.91%) +runt.. + + +runtime.notewakeup (9 samples, 2.51%) +ru.. + + +runtime.schedule (1 samples, 0.28%) + + + +time.Now (5 samples, 1.40%) + + + +runtime.futexwakeup (1 samples, 0.28%) + + + +runtime.gosched_m (1 samples, 0.28%) + + + +runtime.fastrand (1 samples, 0.28%) + + + +runtime.goready (1 samples, 0.28%) + + + +runtime.nanotime (3 samples, 0.84%) + + + +runtime.(*bmap).setoverflow (13 samples, 3.63%) +runt.. + + +runtime.updateTimer0When (1 samples, 0.28%) + + + +runtime.ready (15 samples, 4.19%) +runt.. + + +runtime.lock2 (2 samples, 0.56%) + + + +runtime.heapBitsSetType (2 samples, 0.56%) + + + +runtime.mallocgc (2 samples, 0.56%) + + + +runtime.chansend (1 samples, 0.28%) + + + +runtime.startm (1 samples, 0.28%) + + + +runtime.(*randomEnum).next (1 samples, 0.28%) + + + +runtime.schedule (169 samples, 47.21%) +runtime.schedule + + +runtime.gcAssistAlloc1 (16 samples, 4.47%) +runti.. + + +runtime.goexit0 (2 samples, 0.56%) + + + +runtime.runqget (2 samples, 0.56%) + + + +runtime.advanceEvacuationMark (1 samples, 0.28%) + + + +runtime.markroot (2 samples, 0.56%) + + + +runtime.modtimer (1 samples, 0.28%) + + + +runtime.(*mcentral).grow (3 samples, 0.84%) + + + +sync.(*RWMutex).Lock (4 samples, 1.12%) + + + +runtime.unlockWithRank (2 samples, 0.56%) + + + +runtime.ready (1 samples, 0.28%) + + + +runtime.futexwakeup (1 samples, 0.28%) + + + +runtime.(*mheap).alloc (3 samples, 0.84%) + + + +runtime.scanblock (2 samples, 0.56%) + + + +runtime.mallocgc (3 samples, 0.84%) + + + +runtime.pollWork (1 samples, 0.28%) + + + +runtime.asyncPreempt (1 samples, 0.28%) + + + +aeshashbody (1 samples, 0.28%) + + + +runtime.runqsteal (1 samples, 0.28%) + + + +runtime.heapBits.initSpan (1 samples, 0.28%) + + + +time.Sleep (19 samples, 5.31%) +time.S.. + + +runtime.stopm (21 samples, 5.87%) +runtime.. + + +runtime.systemstack (1 samples, 0.28%) + + + +runtime.osyield (1 samples, 0.28%) + + + +runtime.mallocgc (1 samples, 0.28%) + + + +runtime.futex (1 samples, 0.28%) + + + +runtime.intstring (4 samples, 1.12%) + + + +runtime.nanotime (1 samples, 0.28%) + + + +runtime.siftdownTimer (5 samples, 1.40%) + + + +runtime.mcall (176 samples, 49.16%) +runtime.mcall + + +runtime.futex (1 samples, 0.28%) + + + +all (358 samples, 100%) + + + +runtime.lock2 (1 samples, 0.28%) + + + +runtime.runOneTimer (1 samples, 0.28%) + + + +runtime.notewakeup (48 samples, 13.41%) +runtime.notewakeup + + +runtime.futexsleep (18 samples, 5.03%) +runtim.. + + +runtime.park_m (173 samples, 48.32%) +runtime.park_m + + +main.main.func1.1 (57 samples, 15.92%) +main.main.func1.1 + + +runtime.checkTimers (1 samples, 0.28%) + + + +runtime.acquirep (1 samples, 0.28%) + + + +runtime.unlockWithRank (1 samples, 0.28%) + + + +runtime.arenaIndex (1 samples, 0.28%) + + + +runtime.systemstack (17 samples, 4.75%) +runti.. + + +runtime.write (4 samples, 1.12%) + + + +runtime.nanotime (1 samples, 0.28%) + + + +runtime.(*mcache).refill (1 samples, 0.28%) + + + +runtime.casgstatus (1 samples, 0.28%) + + + +sync.(*Mutex).Unlock (1 samples, 0.28%) + + + +runtime.(*mcentral).grow (1 samples, 0.28%) + + + +runtime.futex (4 samples, 1.12%) + + + +runtime.unlockWithRank (1 samples, 0.28%) + + + +runtime.goready.func1 (1 samples, 0.28%) + + + +runtime.findrunnable (1 samples, 0.28%) + + + +runtime.notesleep (19 samples, 5.31%) +runtim.. + + +runtime.acquirep (1 samples, 0.28%) + + + +main.main.func1.2 (1 samples, 0.28%) + + + +runtime.mstart1 (78 samples, 21.79%) +runtime.mstart1 + + +runtime.heapBits.bits (1 samples, 0.28%) + + + +runtime.read (1 samples, 0.28%) + + + +runtime.(*mcache).nextFree (2 samples, 0.56%) + + + +runtime.notewakeup (1 samples, 0.28%) + + + +runtime.memclrNoHeapPointers (1 samples, 0.28%) + + + +runtime.execute (1 samples, 0.28%) + + + +runtime.newobject (16 samples, 4.47%) +runti.. + + +runtime.sysmon (71 samples, 19.83%) +runtime.sysmon + + +runtime.gcDrain (17 samples, 4.75%) +runti.. + + +runtime.scanframeworker (2 samples, 0.56%) + + + +runtime.newobject (2 samples, 0.56%) + + + +runtime.futexsleep (4 samples, 1.12%) + + + +runtime.goready (15 samples, 4.19%) +runt.. + + +runtime.(*mcentral).cacheSpan (2 samples, 0.56%) + + + +runtime.newarray (3 samples, 0.84%) + + + +runtime.hasPrefix (1 samples, 0.28%) + + + +runtime.stoplockedm (1 samples, 0.28%) + + + +runtime.usleep (13 samples, 3.63%) +runt.. + + +runtime.goroutineReady (15 samples, 4.19%) +runt.. + + +runtime.siftdownTimer (13 samples, 3.63%) +runt.. + + +runtime.lockWithRank (1 samples, 0.28%) + + + +runtime.makeBucketArray (3 samples, 0.84%) + + + +runtime.futex (1 samples, 0.28%) + + + +runtime.startm (39 samples, 10.89%) +runtime.startm + + +runtime.gcBgMarkWorker (17 samples, 4.75%) +runti.. + + +runtime.memclrNoHeapPointers (2 samples, 0.56%) + + + +runtime.startm (9 samples, 2.51%) +ru.. + + +runtime.startm (48 samples, 13.41%) +runtime.startm + + +runtime.futexwakeup (9 samples, 2.51%) +ru.. + + +runtime.(*mheap).alloc.func1 (2 samples, 0.56%) + + + +runtime.gentraceback (2 samples, 0.56%) + + + +runtime.evacuate_faststr (15 samples, 4.19%) +runt.. + + +runtime.rawstring (3 samples, 0.84%) + + + +memeqbody (1 samples, 0.28%) + + + +runtime.gcAssistAlloc.func1 (16 samples, 4.47%) +runti.. + + +runtime.futexwakeup (48 samples, 13.41%) +runtime.futexwakeup + + +runtime.futex (48 samples, 13.41%) +runtime.futex + + +runtime.futex (39 samples, 10.89%) +runtime.futex + + +runtime.futex (9 samples, 2.51%) +ru.. + + +runtime.systemstack (16 samples, 4.47%) +runti.. + + +runtime.notewakeup (39 samples, 10.89%) +runtime.notewakeup + + +runtime.nanotime (1 samples, 0.28%) + + + +runtime.mapiternext (7 samples, 1.96%) +r.. + + +runtime.hashGrow (16 samples, 4.47%) +runti.. + + +runtime.mstart (78 samples, 21.79%) +runtime.mstart + + +runtime.systemstack (2 samples, 0.56%) + + + +runtime.(*mcache).nextFree (3 samples, 0.84%) + + + +runtime.futexsleep (3 samples, 0.84%) + + + +runtime.(*randomEnum).done (1 samples, 0.28%) + + + +runtime.epollwait (12 samples, 3.35%) +run.. + + +runtime.wakep (1 samples, 0.28%) + + + +runtime.scanobject (11 samples, 3.07%) +run.. + + +runtime.checkTimers (8 samples, 2.23%) +r.. + + +runtime.siftdownTimer (1 samples, 0.28%) + + + +runtime.(*mheap).allocSpan (2 samples, 0.56%) + + + +runtime.runtimer (63 samples, 17.60%) +runtime.runtimer + + +runtime.memclrHasPointers (2 samples, 0.56%) + + + +runtime.gcBgMarkWorker.func2 (17 samples, 4.75%) +runti.. + + + diff --git a/dtask/task.go b/dtask/task.go new file mode 100644 index 0000000..50e90c2 --- /dev/null +++ b/dtask/task.go @@ -0,0 +1,40 @@ +package dtask + +import ( + "sync" + "time" +) + +type Task struct { + sync.RWMutex + clock *Clock + jobCache map[string]Job +} + +func NewTask() *Task { + t := &Task{ + clock: NewClock(), + jobCache: make(map[string]Job), + } + return t +} + +func (t *Task) Add(key string, job Job) { + t.Lock() + t.jobCache[key] = job + t.Unlock() +} + +func (t *Task) Get(key string) Job { + t.RLock() + defer t.RUnlock() + return t.jobCache[key] +} + +func (t *Task) Stop() { + t.clock.Stop() +} + +func (t *Task) AddJobRepeat(interval time.Duration, actionMax uint64, jobFunc func()) (jobScheduled Job, inserted bool) { + return t.clock.AddJobRepeat(interval, actionMax, jobFunc) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..771c0bf --- /dev/null +++ b/go.mod @@ -0,0 +1,29 @@ +module gitlab.33.cn/chat/im + +go 1.14 + +require ( + github.com/BurntSushi/toml v0.3.1 + github.com/Terry-Mao/goim v0.0.0-20201205074412-7c5316e1c2e5 + github.com/gin-contrib/pprof v1.3.0 + github.com/gin-gonic/gin v1.6.3 + github.com/golang/protobuf v1.5.2 + github.com/gomodule/redigo v2.0.0+incompatible + github.com/google/uuid v1.1.2 + github.com/gorilla/websocket v1.4.2 + github.com/oofpgDLD/dtask v1.0.2 + github.com/opentracing/opentracing-go v1.2.0 + github.com/rs/zerolog v1.21.0 + github.com/uber/jaeger-client-go v2.30.0+incompatible + github.com/zhenjl/cityhash v0.0.0-20131128155616-cdd6a94144ab + gitlab.33.cn/btrade/auto_trade_tools v1.2.8 + //gitlab.33.cn/chat/im-pkg v0.0.0-00010101000000-000000000000 + gitlab.33.cn/chat/im-pkg v0.0.2 + go.etcd.io/etcd/api/v3 v3.5.0 + go.etcd.io/etcd/client/v3 v3.5.0 + google.golang.org/grpc v1.38.0 + google.golang.org/protobuf v1.26.0 + gopkg.in/Shopify/sarama.v1 v1.19.0 +) + +//replace gitlab.33.cn/chat/im-pkg => ../im-pkg diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..6913769 --- /dev/null +++ b/go.sum @@ -0,0 +1,876 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/33cn/chain33 v0.0.0-20200527072033-e43d8da29c46/go.mod h1:RJsUKcMdXtCgpqp1W1ga6jTCieuJm6n7qd3XmnPXwa4= +github.com/33cn/chain33 v0.0.0-20200605043414-355d96f9ec97/go.mod h1:RJsUKcMdXtCgpqp1W1ga6jTCieuJm6n7qd3XmnPXwa4= +github.com/33cn/chain33 v1.64.0/go.mod h1:RJsUKcMdXtCgpqp1W1ga6jTCieuJm6n7qd3XmnPXwa4= +github.com/33cn/plugin v1.64.1-0.20200529022142-4c8918d6741c/go.mod h1:PKdkkj3I3NoPh2ahQa0cZ4l94PCtG34Bxm8YzaUq1rs= +github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE= +github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +github.com/BurntSushi/toml v0.0.0-20170626110600-a368813c5e64/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= +github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y= +github.com/NebulousLabs/Sia v1.3.7/go.mod h1:SCASk6mV8QdEojKyecjj/Jd0OGSXkZonkhow7XXKk6Q= +github.com/NebulousLabs/entropy-mnemonics v0.0.0-20170316012907-7b01a644a636/go.mod h1:ed2ZsnmJfqVNZOwxWWFZaSHJY3ifOjCS7i5yX9dvKHs= +github.com/NebulousLabs/errors v0.0.0-20171229012116-7ead97ef90b8/go.mod h1:J7tUI9Fg4YuFLsqeLE5uIp93Fot9oBCw2vwZJvLmWso= +github.com/NebulousLabs/fastrand v0.0.0-20180208210444-3cf7173006a0/go.mod h1:Bdzq+51GR4/0DIhaICZEOm+OHvXGwwB2trKZ8B4Y6eQ= +github.com/NebulousLabs/merkletree v0.0.0-20181025040823-2a1d1d1dc33c/go.mod h1:Cn056wBLKay+uIS9LJn7ymwhgC5mqbOtG6iOhEvyy4M= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Shopify/sarama v1.19.0 h1:9oksLxC6uxVPHPVYUmq6xhr1BOF/hHobWH2UzO67z1s= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/Terry-Mao/goim v0.0.0-20201205074412-7c5316e1c2e5 h1:C/AeojfBsXn0IneJFOQdUaVZzp6qs2xa0a72G+EQvyw= +github.com/Terry-Mao/goim v0.0.0-20201205074412-7c5316e1c2e5/go.mod h1:TbixifnJSmnDOkbQHYrXgQ04vqlSq6zlVQ5BWvadH44= +github.com/XiaoMi/pegasus-go-client v0.0.0-20181029071519-9400942c5d1c/go.mod h1:KcL6D/4RZ8RAYzQ5gKI0odcdWUmCVlbQTOlWrhP71CY= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apache/thrift v0.0.0-20171203172758-327ebb6c2b6d/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bilibili/discovery v1.0.1/go.mod h1:daS5nEYEBt0scrrmuoNCxWXDHFK6gtEpjhVKG6MUxUg= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/bsm/sarama-cluster v2.1.15+incompatible h1:RkV6WiNRnqEEbp81druK8zYhmnIgdOjqSVi0+9Cnl2A= +github.com/bsm/sarama-cluster v2.1.15+incompatible/go.mod h1:r7ao+4tTNXvWm+VRpRJchr2kQhqxgmAp2iEX5W96gMM= +github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= +github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= +github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/coreos/bbolt v1.3.0/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.15+incompatible h1:+9RjdC18gMxNQVvSiXvObLu29mOFmkgdsB4cRTlV+EE= +github.com/coreos/etcd v3.3.15+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dchest/blake256 v1.0.0/go.mod h1:xXNWCE1jsAP8DAjP+rKw2MbeqLczjI3TRx2VK+9OEYY= +github.com/decred/base58 v1.0.0/go.mod h1:LLY1p5e3g91byL/UO1eiZaYd+uRoVRarybgcoymu9Ks= +github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= +github.com/dgraph-io/badger v1.6.0-rc1/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0 h1:1NtRmCAqadE2FN4ZcN6g90TP3uk8cg9rn9eNK2197aU= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-resiliency v1.2.0 h1:v7g92e/KSN71Rq7vSThKaWIq68fL4YHvWyiUKorFR1Q= +github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss= +github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/pprof v1.3.0 h1:G9eK6HnbkSqDZBYbzG4wrjCsA4e+cvYAHUZw6W+W9K0= +github.com/gin-contrib/pprof v1.3.0/go.mod h1:waMjT1H9b179t3CxuG1cV3DHpga6ybizwfBaM5OXaB0= +github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v0.0.0-20180512030042-bf7803815b0b/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y= +github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y= +github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= +github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20180424202546-8dffc02ea1cb/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0 h1:bM6ZAFZmc/wPFaRDi0d5L7hGEZEx/2u+Tmr2evNHDiI= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= +github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= +github.com/haltingstate/secp256k1-go v0.0.0-20151224084235-572209b26df6 h1:HE4YDtvtpZgjRJ2tCOmaXlcpBTFG2e0jvfNntM5sXOs= +github.com/haltingstate/secp256k1-go v0.0.0-20151224084235-572209b26df6/go.mod h1:73mKQiY8bLnscfGakn57WAJZTzT0eSUAy3qgMQNR/DI= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= +github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= +github.com/inconshreveable/log15 v0.0.0-20200109203555-b30bc20e4fd1/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb v1.7.9/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= +github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= +github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= +github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= +github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= +github.com/ipfs/go-datastore v0.1.0/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= +github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= +github.com/ipfs/go-ds-badger v0.0.2/go.mod h1:Y3QpeSFWQf6MopLTiZD+VT6IC1yZqaGmjvRcKeSGij8= +github.com/ipfs/go-ds-badger v0.0.5/go.mod h1:g5AuuCGmr7efyzQhLL8MzwqcauPojGPUaHzfGTzuE3s= +github.com/ipfs/go-ds-leveldb v0.0.1/go.mod h1:feO8V3kubwsEF22n0YRQCffeb79OOYIykR4L04tMOYc= +github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= +github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= +github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= +github.com/ipfs/go-todocounter v0.0.1/go.mod h1:l5aErvQc8qKE2r7NDMjmq5UNAvuZy0rC8BHOplkWvZ4= +github.com/issue9/assert v1.0.0/go.mod h1:KLwR3U/5rbCxqwAnV3aCr+dz07aoIyIfk2lefIVr2BA= +github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= +github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jbenet/go-cienv v0.0.0-20150120210510-1bb1476777ec/go.mod h1:rGaEvXB4uRSZMmzKNLoXvTu1sfx+1kv/DojUlPrSZGs= +github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= +github.com/jbenet/go-temp-err-catcher v0.0.0-20150120210811-aac704a3f4f2/go.mod h1:8GXXJV31xl8whumTzdZsTt3RnUIiPqzkyf7mxToRCMs= +github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY= +github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/json-iterator/go v0.0.0-20180526014329-8744d7c5c7b4/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ= +github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= +github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= +github.com/libp2p/go-conn-security-multistream v0.1.0/go.mod h1:aw6eD7LOsHEX7+2hJkDxw1MteijaVcI+/eP2/x3J1xc= +github.com/libp2p/go-eventbus v0.0.2/go.mod h1:Hr/yGlwxA/stuLnpMiu82lpNKpvRy3EaJxPu40XYOwk= +github.com/libp2p/go-eventbus v0.1.0/go.mod h1:vROgu5cs5T7cv7POWlWxBaVLxfSegC5UGQf8A2eEmx4= +github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8= +github.com/libp2p/go-flow-metrics v0.0.2/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= +github.com/libp2p/go-libp2p v0.3.1/go.mod h1:e6bwxbdYH1HqWTz8faTChKGR0BjPc8p+6SyP8GTTR7Y= +github.com/libp2p/go-libp2p v0.4.0/go.mod h1:9EsEIf9p2UDuwtPd0DwJsAl0qXVxgAnuDGRvHbfATfI= +github.com/libp2p/go-libp2p-autonat v0.1.0/go.mod h1:1tLf2yXxiE/oKGtDwPYWTSYG3PtvYlJmg7NeVtPRqH8= +github.com/libp2p/go-libp2p-blankhost v0.1.1/go.mod h1:pf2fvdLJPsC1FsVrNP3DUUvMzUts2dsLLBEpo1vW1ro= +github.com/libp2p/go-libp2p-blankhost v0.1.3/go.mod h1:KML1//wiKR8vuuJO0y3LUd1uLv+tlkGTAr3jC0S5cLg= +github.com/libp2p/go-libp2p-blankhost v0.1.4/go.mod h1:oJF0saYsAXQCSfDq254GMNmLNz6ZTHTOvtF4ZydUvwU= +github.com/libp2p/go-libp2p-circuit v0.1.1/go.mod h1:Ahq4cY3V9VJcHcn1SBXjr78AbFkZeIRmfunbA7pmFh8= +github.com/libp2p/go-libp2p-circuit v0.1.3/go.mod h1:Xqh2TjSy8DD5iV2cCOMzdynd6h8OTBGoV1AWbWor3qM= +github.com/libp2p/go-libp2p-connmgr v0.2.0/go.mod h1:C4r2FWWfbfk7U9NFcvY8oegIVxK3jPtOwFrEmtDunTg= +github.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco= +github.com/libp2p/go-libp2p-core v0.0.4/go.mod h1:jyuCQP356gzfCFtRKyvAbNkyeuxb7OlyhWZ3nls5d2I= +github.com/libp2p/go-libp2p-core v0.0.6/go.mod h1:0d9xmaYAVY5qmbp/fcgxHT3ZJsLjYeYPMJAUKpaCHrE= +github.com/libp2p/go-libp2p-core v0.2.0/go.mod h1:X0eyB0Gy93v0DZtSYbEM7RnMChm9Uv3j7yRXjO77xSI= +github.com/libp2p/go-libp2p-core v0.2.2/go.mod h1:8fcwTbsG2B+lTgRJ1ICZtiM5GWCWZVoVrLaDRvIRng0= +github.com/libp2p/go-libp2p-core v0.2.3/go.mod h1:GqhyQqyIAPsxFYXHMjfXgMv03lxsvM0mFzuYA9Ib42A= +github.com/libp2p/go-libp2p-core v0.2.5/go.mod h1:6+5zJmKhsf7yHn1RbmYDu08qDUpIUxGdqHuEZckmZOA= +github.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI= +github.com/libp2p/go-libp2p-discovery v0.1.0/go.mod h1:4F/x+aldVHjHDHuX85x1zWoFTGElt8HnoDzwkFZm29g= +github.com/libp2p/go-libp2p-kad-dht v0.2.1/go.mod h1:k7ONOlup7HKzQ68dE6lSnp07cdxdkmnRa+6B4Fh9/w0= +github.com/libp2p/go-libp2p-kbucket v0.2.1/go.mod h1:/Rtu8tqbJ4WQ2KTCOMJhggMukOLNLNPY1EtEWWLxUvc= +github.com/libp2p/go-libp2p-loggables v0.1.0/go.mod h1:EyumB2Y6PrYjr55Q3/tiJ/o3xoDasoRYM7nOzEpoa90= +github.com/libp2p/go-libp2p-mplex v0.2.0/go.mod h1:Ejl9IyjvXJ0T9iqUTE1jpYATQ9NM3g+OtR+EMMODbKo= +github.com/libp2p/go-libp2p-mplex v0.2.1/go.mod h1:SC99Rxs8Vuzrf/6WhmH41kNn13TiYdAWNYHrwImKLnE= +github.com/libp2p/go-libp2p-nat v0.0.4/go.mod h1:N9Js/zVtAXqaeT99cXgTV9e75KpnWCvVOiGzlcHmBbY= +github.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU= +github.com/libp2p/go-libp2p-peer v0.2.0/go.mod h1:RCffaCvUyW2CJmG2gAWVqwePwW7JMgxjsHm7+J5kjWY= +github.com/libp2p/go-libp2p-peerstore v0.1.0/go.mod h1:2CeHkQsr8svp4fZ+Oi9ykN1HBb6u0MOvdJ7YIsmcwtY= +github.com/libp2p/go-libp2p-peerstore v0.1.3/go.mod h1:BJ9sHlm59/80oSkpWgr1MyY1ciXAXV397W6h1GH/uKI= +github.com/libp2p/go-libp2p-record v0.1.1/go.mod h1:VRgKajOyMVgP/F0L5g3kH7SVskp17vFi2xheb5uMJtg= +github.com/libp2p/go-libp2p-routing v0.1.0/go.mod h1:zfLhI1RI8RLEzmEaaPwzonRvXeeSHddONWkcTcB54nE= +github.com/libp2p/go-libp2p-secio v0.1.0/go.mod h1:tMJo2w7h3+wN4pgU2LSYeiKPrfqBgkOsdiKK77hE7c8= +github.com/libp2p/go-libp2p-secio v0.2.0/go.mod h1:2JdZepB8J5V9mBp79BmwsaPQhRPNN2NrnB2lKQcdy6g= +github.com/libp2p/go-libp2p-swarm v0.1.0/go.mod h1:wQVsCdjsuZoc730CgOvh5ox6K8evllckjebkdiY5ta4= +github.com/libp2p/go-libp2p-swarm v0.2.1/go.mod h1:x07b4zkMFo2EvgPV2bMTlNmdQc8i+74Jjio7xGvsTgU= +github.com/libp2p/go-libp2p-swarm v0.2.2/go.mod h1:fvmtQ0T1nErXym1/aa1uJEyN7JzaTNyBcHImCxRpPKU= +github.com/libp2p/go-libp2p-testing v0.0.2/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= +github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= +github.com/libp2p/go-libp2p-testing v0.0.4/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= +github.com/libp2p/go-libp2p-testing v0.1.0/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0= +github.com/libp2p/go-libp2p-transport-upgrader v0.1.1/go.mod h1:IEtA6or8JUbsV07qPW4r01GnTenLW4oi3lOPbUMGJJA= +github.com/libp2p/go-libp2p-yamux v0.2.0/go.mod h1:Db2gU+XfLpm6E4rG5uGCFX6uXA8MEXOxFcRoXUODaK8= +github.com/libp2p/go-libp2p-yamux v0.2.1/go.mod h1:1FBXiHDk1VyRM1C0aez2bCfHQ4vMZKkAQzZbkSQt5fI= +github.com/libp2p/go-maddr-filter v0.0.4/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q= +github.com/libp2p/go-maddr-filter v0.0.5/go.mod h1:Jk+36PMfIqCJhAnaASRH83bdAvfDRp/w6ENFaC9bG+M= +github.com/libp2p/go-mplex v0.0.3/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0= +github.com/libp2p/go-mplex v0.1.0/go.mod h1:SXgmdki2kwCUlCCbfGLEgHjC4pFqhTp0ZoV6aiKgxDU= +github.com/libp2p/go-msgio v0.0.2/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= +github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= +github.com/libp2p/go-nat v0.0.3/go.mod h1:88nUEt0k0JD45Bk93NIwDqjlhiOwOoV36GchpcVc1yI= +github.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIYDXnvOXkc0= +github.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= +github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA= +github.com/libp2p/go-reuseport-transport v0.0.2/go.mod h1:YkbSDrvjUVDL6b8XqriyA20obEtsW9BLkuOUyQAOCbs= +github.com/libp2p/go-stream-muxer v0.0.1/go.mod h1:bAo8x7YkSpadMTbtTaxGVHWUQsR/l5MEaHbKaliuT14= +github.com/libp2p/go-stream-muxer-multistream v0.2.0/go.mod h1:j9eyPol/LLRqT+GPLSxvimPhNph4sfYfMoDPd7HkzIc= +github.com/libp2p/go-tcp-transport v0.1.0/go.mod h1:oJ8I5VXryj493DEJ7OsBieu8fcg2nHGctwtInJVpipc= +github.com/libp2p/go-tcp-transport v0.1.1/go.mod h1:3HzGvLbx6etZjnFlERyakbaYPdfjg2pWP97dFZworkY= +github.com/libp2p/go-ws-transport v0.1.0/go.mod h1:rjw1MG1LU9YDC6gzmwObkPd/Sqwhw7yT74kj3raBFuo= +github.com/libp2p/go-ws-transport v0.1.2/go.mod h1:dsh2Ld8F+XNmzpkaAijmg5Is+e9l6/1tK/6VFOdN69Y= +github.com/libp2p/go-yamux v1.2.2/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= +github.com/libp2p/go-yamux v1.2.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= +github.com/lotus-king/SM3 v0.0.0-20161018102718-5a3b85ca5a40/go.mod h1:BZEQO3B4MHBIfALOrsqsiZ1hNMdVZ0j7E4q6zXLh2aM= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= +github.com/minio/sha256-simd v0.0.0-20190328051042-05b4dd3047e5/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= +github.com/minio/sha256-simd v0.1.0/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= +github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180511053014-58118c1ea916/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= +github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= +github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= +github.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= +github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= +github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= +github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= +github.com/multiformats/go-multiaddr-dns v0.0.3/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= +github.com/multiformats/go-multiaddr-dns v0.1.0/go.mod h1:01k2RAqtoXIuPa3DCavAE9/6jc6nM0H3EgZyfUhN2oY= +github.com/multiformats/go-multiaddr-fmt v0.0.1/go.mod h1:aBYjqL4T/7j4Qx+R73XSv/8JsgnRFlf0w2KGLCmXl3Q= +github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= +github.com/multiformats/go-multiaddr-net v0.0.1/go.mod h1:nw6HSxNmCIQH27XPGBuX+d1tnvM7ihcFwHMSstNAVUU= +github.com/multiformats/go-multiaddr-net v0.1.0/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= +github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= +github.com/multiformats/go-multicodec v0.1.6/go.mod h1:lliaRHbcG8q33yf4Ot9BGD7JqR/Za9HE7HTyVyKwrUQ= +github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= +github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po= +github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= +github.com/multiformats/go-multistream v0.1.0/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= +github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/oofpgDLD/dtask v1.0.2 h1:q7LvdqCKZocuazc1cPWDXqv6PZaRvCAU/KTkESf7wxs= +github.com/oofpgDLD/dtask v1.0.2/go.mod h1:r07DPvHuXY0iEq7QSFBZ5gVYRmi3Ohv6d8rek5CNPuk= +github.com/opentracing/opentracing-go v1.0.2 h1:3jA2P6O1F9UOrWVpwrIo17pu01KWvNWg4X946/Y5Zwg= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= +github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563 h1:dY6ETXrvDG7Sa4vE8ZQG4yqWg6UnOcbqTAahkV813vQ= +github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/robertkrimen/otto v0.0.0-20180617131154-15f95af6e78d/go.mod h1:xvqspoSXJTIpemEonrMDFq6XzwHYYgToXWj5eRX1OtY= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.21.0 h1:Q3vdXlfLNT+OftyBHsU0Y445MD+8m8axjKgf2si0QcM= +github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/smartystreets/assertions v0.0.0-20180301161246-7678a5452ebe/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= +github.com/smartystreets/gunit v0.0.0-20180314194857-6f0d6275bdcd/go.mod h1:XUKj4gbqj2QvJk/OdLWzyZ3FYli0f+MdpngyryX0gcw= +github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY= +github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0= +github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/tendermint/ed25519 v0.0.0-20171027050219-d8387025d2b9/go.mod h1:nt45hbhDkWVdMBkr2TOgOzCrpBccXdN09WOiOYTHVEk= +github.com/thinkboy/log4go v0.0.0-20160303045050-f91a411e4a18/go.mod h1:IeFvD+ls8ldW9O62n+3QrktXSvtXuUDi6iLnISh6q80= +github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/uber/jaeger-client-go v2.28.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= +github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= +github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/ugorji/go v0.0.0-20180407103000-f3cacc17c85e/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= +github.com/ugorji/go v1.1.2/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.5.0/go.mod h1:eriCz9OhZjKCGfJ185a/IDgNl0bg9IbzfpcslMZXU1c= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc/go.mod h1:r45hJU7yEoA81k6MWNhpMj/kms0n14dkzkxYHoB96UM= +github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= +github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= +github.com/whyrusleeping/go-notifier v0.0.0-20170827234753-097c5d47330f/go.mod h1:cZNvX9cFybI01GriPRMXDtczuvUhgbcYr9iCGaNlRv8= +github.com/whyrusleeping/mafmt v1.2.8/go.mod h1:faQJFPbLSxzD9xpA02ttW/tS9vZykNvXwGvqIpk20FA= +github.com/whyrusleeping/mdns v0.0.0-20180901202407-ef14215e6b30/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= +github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= +github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI= +github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/zhenjl/cityhash v0.0.0-20131128155616-cdd6a94144ab h1:BWHvAOZz0pBILkGl/ebPQKZDrqbaWj/iN9RE8AvaTvg= +github.com/zhenjl/cityhash v0.0.0-20131128155616-cdd6a94144ab/go.mod h1:P6L88wrqK99Njntah9SB7AyzFpUXsXYq06LkjixxQmY= +gitlab.33.cn/btrade/auto_trade_tools v1.2.8 h1:3FSdvO9FRs4VKP66XlypbWteo91klcsWxbScbqhCFOI= +gitlab.33.cn/btrade/auto_trade_tools v1.2.8/go.mod h1:XbudTl/FDkpdcGDKwDIZ0QTuY2r9NVElcE/e9afixv8= +gitlab.33.cn/btrade/blockchain v1.1.0/go.mod h1:4XRBV9vjiAB2tQbXAtWCoAcI55OmNIVu9Gc8yrzg1cM= +gitlab.33.cn/btrade/common v1.0.2/go.mod h1:BJhb/g0OgtLBNJxS8zZA54jTtr6oY0/T5N5jTKbIxLY= +gitlab.33.cn/btrade/crypto v1.0.1/go.mod h1:FYhKLN69inc9nhXJw49qodvRRmFQCqEyj/JhfeNLfZk= +gitlab.33.cn/btrade/log v1.0.2/go.mod h1:grfvrYpf0OJOJ5HCUefcPlaOunEc53wOXw4WnAHLNwM= +gitlab.33.cn/chat/im-pkg v0.0.1 h1:kFU4SVq7xC0lvufGv4zjTzVKn5BPjqa0Am5IYWPKbOU= +gitlab.33.cn/chat/im-pkg v0.0.1/go.mod h1:5kmgKeovttxiKqaNqGQovwIm3v3ZZWPxD/xGj8teBj0= +gitlab.33.cn/chat/im-pkg v0.0.2 h1:f4EUCFoGNaxTYRCoE8fLpCt+G2MGTMbKU/TCCauEkhM= +gitlab.33.cn/chat/im-pkg v0.0.2/go.mod h1:5kmgKeovttxiKqaNqGQovwIm3v3ZZWPxD/xGj8teBj0= +gitlab.33.cn/contract/exchange v1.0.7/go.mod h1:MylnHconA4rUfp10jWZc6zZ+3LBIt13Ow9HUWGACAto= +go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd/api/v3 v3.5.0 h1:GsV3S+OfZEOCNXdtNkBSR7kgLobAa/SO6tCxRa0GAYw= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0 h1:2aQv6F436YnN7I4VbI8PPYrBhu+SmrTaADcf8Mi/6PU= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v3 v3.5.0 h1:62Eh0XOro+rDwkrypAGDfgmNh5Joq+z+W9HZdlXMzek= +go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= +go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.2.0 h1:6I+W7f5VwC5SV9dNrZ3qXrDB9mD0dyGOi/ZJmYw03T4= +go.uber.org/multierr v1.2.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8Xb6W51gRwfDBhkdcaw= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0 h1:xQwXv67TxFo9nC1GJFyab5eq/5B590r6RlnL/G8Sz7w= +golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM= +gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200310143817-43be25429f5a h1:lRlI5zu6AFy3iU/F8YWyNrAmn/tPCnhiTxfwhWb76eU= +google.golang.org/genproto v0.0.0-20200310143817-43be25429f5a/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/grpc v0.0.0-20181030232906-a88340f3c899/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/Shopify/sarama.v1 v1.19.0 h1:yvI/R1jfMpKvvwmX4r/AQjaI5oszWEOlvKxUdaj53OM= +gopkg.in/Shopify/sarama.v1 v1.19.0/go.mod h1:AxnvoaevB2nBjNK17cG61A3LleFcWFwVBHBt+cot4Oc= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ= +gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= +gopkg.in/go-playground/webhooks.v5 v5.2.0/go.mod h1:LZbya/qLVdbqDR1aKrGuWV6qbia2zCYSR5dpom2SInQ= +gopkg.in/h2non/gock.v1 v1.0.8/go.mod h1:KHI4Z1sxDW6P4N3DfTWSEza07YpkQP7KJBfglRMEjKY= +gopkg.in/natefinch/lumberjack.v2 v2.0.0-20170531160350-a96e63847dc3/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78= +gopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d/go.mod h1:z+K8VcOYVYcSwSjGebuDL6176A1XskgbtNl64NSg+n8= +gopkg.in/src-d/go-log.v1 v1.0.1/go.mod h1:GN34hKP0g305ysm2/hctJ0Y8nWP3zxXXJ8GFabTyABE= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637/go.mod h1:BHsqpu/nsuzkT5BpiH1EMZPLyqSMM8JbIavyFACoFNk= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/logic/CHANGELOG.md b/logic/CHANGELOG.md new file mode 100644 index 0000000..84219e1 --- /dev/null +++ b/logic/CHANGELOG.md @@ -0,0 +1,38 @@ +版本号`major.minor.patch`具体规则如下: +- major:主版本号,如有重大版本重构则该字段递增,通常各主版本间接口不兼容。 +- minor:次版本号,各次版本号间接口保持兼容,如有接口新增或优化则该字段递增。 +- patch:补丁号,如有功能改善或缺陷修复则该字段递增。 + +## version 3.1.4 @2021.10.28 + +优化 log 模块 + +配置文件更新 +新增 +```toml +[log] + Level="debug" + Mode="console" + Path="" + Display="json" +``` + +## version 3.1.3 @2021.7.29 + +修复当 addr 为空产生 panic + +## version 3.1.2 @2021.7.22 + +**Feature** +- 支持根据 server 使用指定位置的 comet rpc @3.1.2 2021.7.22 + + +## example x.x.x @yy.mm.dd + +**Feature** + +**Bug Fixes** + +**Improvement** + +**Breaking Change** diff --git a/logic/auth/auth.go b/logic/auth/auth.go new file mode 100644 index 0000000..cd396df --- /dev/null +++ b/logic/auth/auth.go @@ -0,0 +1,23 @@ +package auth + +import "time" + +var execAuth = make(map[string]CreateFunc) + +type CreateFunc func(url string, timeout time.Duration) Auth + +func Register(name string, exec CreateFunc) { + execAuth[name] = exec +} + +func Load(name string) (CreateFunc, error) { + exec, ok := execAuth[name] + if !ok { + return nil, nil + } + return exec, nil +} + +type Auth interface { + DoAuth(token string) (string, error) +} diff --git a/logic/auth/dtalk/auth.go b/logic/auth/dtalk/auth.go new file mode 100644 index 0000000..cfa37b5 --- /dev/null +++ b/logic/auth/dtalk/auth.go @@ -0,0 +1,67 @@ +package acc + +import ( + "encoding/json" + "errors" + "time" + + "gitlab.33.cn/btrade/auto_trade_tools/reqtypes" + "gitlab.33.cn/btrade/auto_trade_tools/util" +) + +type talkClient struct { + url string + timeout time.Duration +} + +func (a *talkClient) DoAuth(token string) (uid string, err error) { + var ( + bytes []byte + ) + headers := map[string]string{} + headers["FZM-SIGNATURE"] = token + bytes, err = util.HttpReq(&reqtypes.HttpParams{ + Method: "POST", + ReqUrl: a.url, + HeaderMap: headers, + Timeout: a.timeout, + }) + if err != nil { + return + } + + var res map[string]interface{} + err = json.Unmarshal(bytes, &res) + if err != nil { + return + } + + if e, ok := res["error"]; ok { + err = errors.New(e.(string)) + return + } + + if _, ok := res["data"]; !ok { + err = errors.New("invalid auth res") + return + } + + data, ok := res["data"].(map[string]interface{}) + if !ok { + err = errors.New("invalid auth data format") + return + } + + if _, ok := data["address"]; !ok { + err = errors.New("invalid auth data") + return + } + + uid, ok = data["address"].(string) + if !ok { + err = errors.New("invalid auth data id format") + return + } + + return +} diff --git a/logic/auth/dtalk/plugin.go b/logic/auth/dtalk/plugin.go new file mode 100644 index 0000000..dc26420 --- /dev/null +++ b/logic/auth/dtalk/plugin.go @@ -0,0 +1,17 @@ +package acc + +import ( + "time" + + "gitlab.33.cn/chat/im/logic/auth" +) + +const Name = "dtalk" + +func init() { + auth.Register(Name, NewAuth) +} + +func NewAuth(url string, timeout time.Duration) auth.Auth { + return &talkClient{url: url, timeout: timeout} +} diff --git a/logic/auth/zhaobi/auth.go b/logic/auth/zhaobi/auth.go new file mode 100644 index 0000000..4f849a4 --- /dev/null +++ b/logic/auth/zhaobi/auth.go @@ -0,0 +1,67 @@ +package acc + +import ( + "encoding/json" + "errors" + "time" + + "gitlab.33.cn/btrade/auto_trade_tools/reqtypes" + "gitlab.33.cn/btrade/auto_trade_tools/util" +) + +type talkClient struct { + url string + timeout time.Duration +} + +func (a *talkClient) DoAuth(token string) (uid string, err error) { + var ( + bytes []byte + ) + headers := map[string]string{} + headers["Authorization"] = token + bytes, err = util.HttpReq(&reqtypes.HttpParams{ + Method: "GET", + ReqUrl: a.url, + HeaderMap: headers, + Timeout: a.timeout, + }) + if err != nil { + return + } + + var res map[string]interface{} + err = json.Unmarshal(bytes, &res) + if err != nil { + return + } + + if e, ok := res["error"]; ok { + err = errors.New(e.(string)) + return + } + + if _, ok := res["data"]; !ok { + err = errors.New("invalid auth res") + return + } + + data, ok := res["data"].(map[string]interface{}) + if !ok { + err = errors.New("invalid auth data format") + return + } + + if _, ok := data["user_id"]; !ok { + err = errors.New("invalid auth data") + return + } + + uid, ok = data["user_id"].(string) + if !ok { + err = errors.New("invalid auth data id format") + return + } + + return +} diff --git a/logic/auth/zhaobi/plugin.go b/logic/auth/zhaobi/plugin.go new file mode 100644 index 0000000..728e60f --- /dev/null +++ b/logic/auth/zhaobi/plugin.go @@ -0,0 +1,17 @@ +package acc + +import ( + "time" + + "gitlab.33.cn/chat/im/logic/auth" +) + +const Name = "zb_otc" + +func init() { + auth.Register(Name, NewAuth) +} + +func NewAuth(url string, timeout time.Duration) auth.Auth { + return &talkClient{url: url, timeout: timeout} +} diff --git a/logic/cmd/main.go b/logic/cmd/main.go new file mode 100644 index 0000000..f7bf001 --- /dev/null +++ b/logic/cmd/main.go @@ -0,0 +1,130 @@ +package main + +import ( + "encoding/json" + "flag" + "fmt" + "net" + "net/http" + _ "net/http/pprof" + "os" + "os/signal" + "syscall" + + "github.com/Terry-Mao/goim/pkg/ip" + "github.com/opentracing/opentracing-go" + "github.com/rs/zerolog/log" + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/config" + xlog "gitlab.33.cn/chat/im-pkg/log" + "gitlab.33.cn/chat/im-pkg/trace" + "gitlab.33.cn/chat/im/logic" + _ "gitlab.33.cn/chat/im/logic/auth/dtalk" + _ "gitlab.33.cn/chat/im/logic/auth/zhaobi" + "gitlab.33.cn/chat/im/logic/conf" + "gitlab.33.cn/chat/im/logic/grpc" + "gitlab.33.cn/chat/im/naming" +) + +const ( + srvName = "logic" +) + +var ( + debug bool +) + +func init() { + flag.BoolVar(&debug, "debug", false, "sets log level to debug") +} + +var ( + // projectVersion 项目版本 + projectVersion = "3.1.4" + // goVersion go版本 + goVersion = "" + // gitCommit git提交commit id + gitCommit = "" + // buildTime 编译时间 + buildTime = "" + + isShowVersion = flag.Bool("version", false, "show project version") +) + +// showVersion 显示项目版本信息 +func showVersion(isShow bool) { + if isShow { + fmt.Printf("Project: %s\n", srvName) + fmt.Printf(" Version: %s\n", projectVersion) + fmt.Printf(" Go Version: %s\n", goVersion) + fmt.Printf(" Git Commit: %s\n", gitCommit) + fmt.Printf(" Build Time: %s\n", buildTime) + os.Exit(0) + } +} + +func main() { + flag.Parse() + showVersion(*isShowVersion) + + if err := conf.Init(); err != nil { + panic(err) + } + + //log init + var err error + log.Logger, err = xlog.Init(conf.Conf.Log) + if err != nil { + panic(err) + } + log.Logger.With().Str("service", srvName) + + byte, _ := json.Marshal(conf.Conf) + log.Info().Str("config", string(byte)).Send() + + // trace init + tracer, tracerCloser := trace.Init(srvName, conf.Conf.Trace, config.Logger(jaeger.NullLogger)) + //不然后续不会有Jaeger实例 + opentracing.SetGlobalTracer(tracer) + + srv := logic.New(conf.Conf) + rpcSrv := grpc.New(conf.Conf.RPCServer, srv) + + go func() { + if err := http.ListenAndServe(":8001", nil); err != nil { + panic(err) + } + }() + + // register logic + _, port, _ := net.SplitHostPort(conf.Conf.RPCServer.Addr) + addr := fmt.Sprintf("%s:%s", ip.InternalIP(), port) + if err := naming.Register(conf.Conf.Reg.RegAddrs, conf.Conf.Reg.SrvName, addr, conf.Conf.Reg.Schema, 15); err != nil { + panic(err) + } + fmt.Println("register ok") + + // signal + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) + for { + s := <-c + log.Info().Str("signal", s.String()).Send() + switch s { + case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT: + if err := naming.UnRegister(conf.Conf.Reg.SrvName, addr, conf.Conf.Reg.Schema); err != nil { + log.Error().Err(err).Msg("naming.UnRegister") + } + srv.Close() + rpcSrv.GracefulStop() + if err := tracerCloser.Close(); err != nil { + log.Error().Err(err).Msg("tracer close failed") + } + //log.Flush() + return + case syscall.SIGHUP: + default: + return + } + } +} diff --git a/logic/cmd/run.sh b/logic/cmd/run.sh new file mode 100644 index 0000000..8612c33 --- /dev/null +++ b/logic/cmd/run.sh @@ -0,0 +1,6 @@ +# -log_dir=log 日志输出文件夹 +# -logtostderr=true 打印到标准错误而不是文件。 +# -alsologtostderr=true 同时打印到标准错误。 +# -v=4 设置日志级别 +# -vmodule=main=5 设置单个文件日志级别 +./main -v=4 -log_dir=log -alsologtostderr=true \ No newline at end of file diff --git a/logic/conf/conf.go b/logic/conf/conf.go new file mode 100644 index 0000000..af884f7 --- /dev/null +++ b/logic/conf/conf.go @@ -0,0 +1,164 @@ +package conf + +import ( + "flag" + "os" + "time" + + "github.com/BurntSushi/toml" + xtime "github.com/Terry-Mao/goim/pkg/time" + "github.com/uber/jaeger-client-go" + traceConfig "github.com/uber/jaeger-client-go/config" + xlog "gitlab.33.cn/chat/im-pkg/log" +) + +var ( + confPath string + regAddrs string + + // Conf config + Conf *Config +) + +func init() { + var ( + defAddrs = os.Getenv("REGADDRS") + ) + flag.StringVar(&confPath, "conf", "logic.toml", "default config path") + flag.StringVar(®Addrs, "reg", defAddrs, "etcd register addrs. eg:127.0.0.1:2379") +} + +// Init init config. +func Init() (err error) { + Conf = Default() + _, err = toml.DecodeFile(confPath, &Conf) + return +} + +// Default new a config with specified defualt value. +func Default() *Config { + return &Config{ + Env: "", + Log: xlog.Config{ + Level: "debug", + Mode: "console", + Path: "", + Display: "console", + }, + Trace: traceConfig.Configuration{ + ServiceName: "answer", + Gen128Bit: true, + Sampler: &traceConfig.SamplerConfig{ + Type: jaeger.SamplerTypeConst, + Param: 1, + }, + Reporter: &traceConfig.ReporterConfig{ + LogSpans: true, + LocalAgentHostPort: "127.0.0.1:6831", + }, + }, + Reg: &Reg{ + Schema: "im", + SrvName: "logic", + RegAddrs: regAddrs, + }, + CometRPCClient: &RPCClient{ + Schema: "im", + SrvName: "comet", + Dial: xtime.Duration(time.Second), + Timeout: xtime.Duration(time.Second)}, + RPCServer: &RPCServer{ + Network: "tcp", + Addr: "3119", + Timeout: xtime.Duration(time.Second), + IdleTimeout: xtime.Duration(time.Second * 60), + MaxLifeTime: xtime.Duration(time.Hour * 2), + ForceCloseWait: xtime.Duration(time.Second * 20), + KeepAliveInterval: xtime.Duration(time.Second * 60), + KeepAliveTimeout: xtime.Duration(time.Second * 20), + }, + Backoff: &Backoff{MaxDelay: 300, BaseDelay: 3, Factor: 1.8, Jitter: 1.3}, + } +} + +// Config config. +type Config struct { + Env string + Log xlog.Config + Trace traceConfig.Configuration + Reg *Reg + CometRPCClient *RPCClient + RPCServer *RPCServer + Kafka *Kafka + Redis *Redis + Node *Node + Backoff *Backoff + Apps []*App +} + +// Reg is service register/discovery config +type Reg struct { + Schema string + SrvName string // call + RegAddrs string // etcd addrs, seperate by ',' +} + +type App struct { + AppId string + AuthUrl string + Timeout xtime.Duration +} + +// Node node config. +type Node struct { + HeartbeatMax int + Heartbeat xtime.Duration +} + +// Backoff backoff. +type Backoff struct { + MaxDelay int32 + BaseDelay int32 + Factor float32 + Jitter float32 +} + +// Redis . +type Redis struct { + Network string + Addr string + Auth string + Active int + Idle int + DialTimeout xtime.Duration + ReadTimeout xtime.Duration + WriteTimeout xtime.Duration + IdleTimeout xtime.Duration + Expire xtime.Duration +} + +// Kafka . +type Kafka struct { + Topic string + Brokers []string +} + +// RPCClient is RPC client config. +type RPCClient struct { + Schema string + SrvName string + Dial xtime.Duration + Timeout xtime.Duration +} + +// RPCServer is RPC server config. +type RPCServer struct { + Network string + Addr string + Timeout xtime.Duration + IdleTimeout xtime.Duration + MaxLifeTime xtime.Duration + ForceCloseWait xtime.Duration + KeepAliveInterval xtime.Duration + KeepAliveTimeout xtime.Duration +} diff --git a/logic/conf/logic.toml b/logic/conf/logic.toml new file mode 100644 index 0000000..8f974f1 --- /dev/null +++ b/logic/conf/logic.toml @@ -0,0 +1,69 @@ +env="debug" + +[log] + Level="debug" + Mode="console" + Path="" + Display="json" + +[Trace] + ServiceName="" + Gen128Bit=true +[Trace.Sampler] + Type="const" + Param=1.0 +[Trace.Reporter] + LogSpans=true + LocalAgentHostPort="172.16.101.130:6831" + +[reg] + schema = "im" + srvName = "logic" + regAddrs = "127.0.0.1:2379" + +[node] + heartbeat = "4m" + heartbeatMax = 2 + +[backoff] + maxDelay = 300 + baseDelay = 3 + factor = 1.8 + jitter = 0.3 + +[RPCServer] + Network = "tcp" + Addr = ":3119" + Timeout = "1s" + KeepAliveMaxConnectionIdle = "60s" + KeepAliveMaxConnectionAge = "2h" + KeepAliveMaxMaxConnectionAgeGrace = "20s" + KeepAliveTime = "60s" + KeepAliveTimeout = "20s" + + +[CometRPCClient] + schema = "im" + srvName = "comet" + dial = "1s" + timeout = "1s" + +[kafka] + topic = "goim-push-topic" + brokers = ["127.0.0.1:9092"] + +[redis] + network = "tcp" + addr = "127.0.0.1:6379" + active = 60000 + idle = 1024 + dialTimeout = "200ms" + readTimeout = "500ms" + writeTimeout = "500ms" + idleTimeout = "120s" + expire = "30m" + +[[apps]] + appId = "dtalk" + authUrl = "http://127.0.0.1:18002/user/login" + timeout = "1s" \ No newline at end of file diff --git a/logic/dao/dao.go b/logic/dao/dao.go new file mode 100644 index 0000000..d28ffbc --- /dev/null +++ b/logic/dao/dao.go @@ -0,0 +1,72 @@ +package dao + +import ( + "context" + "time" + + "github.com/gomodule/redigo/redis" + "gitlab.33.cn/chat/im/logic/conf" + kafka "gopkg.in/Shopify/sarama.v1" +) + +// Dao dao. +type Dao struct { + c *conf.Config + kafkaPub kafka.SyncProducer + redis *redis.Pool + redisExpire int32 +} + +// New new a dao and return. +func New(c *conf.Config) *Dao { + d := &Dao{ + c: c, + kafkaPub: newKafkaPub(c.Kafka), + redis: newRedis(c.Redis), + redisExpire: int32(time.Duration(c.Redis.Expire) / time.Second), + } + return d +} + +func newKafkaPub(c *conf.Kafka) kafka.SyncProducer { + kc := kafka.NewConfig() + kc.Producer.RequiredAcks = kafka.WaitForAll // Wait for all in-sync replicas to ack the message + kc.Producer.Retry.Max = 10 // Retry up to 10 times to produce the message + kc.Producer.Return.Successes = true + kc.Version = kafka.V0_11_0_2 + pub, err := kafka.NewSyncProducer(c.Brokers, kc) + if err != nil { + panic(err) + } + return pub +} + +func newRedis(c *conf.Redis) *redis.Pool { + return &redis.Pool{ + MaxIdle: c.Idle, + MaxActive: c.Active, + IdleTimeout: time.Duration(c.IdleTimeout), + Dial: func() (redis.Conn, error) { + conn, err := redis.Dial(c.Network, c.Addr, + redis.DialConnectTimeout(time.Duration(c.DialTimeout)), + redis.DialReadTimeout(time.Duration(c.ReadTimeout)), + redis.DialWriteTimeout(time.Duration(c.WriteTimeout)), + redis.DialPassword(c.Auth), + ) + if err != nil { + return nil, err + } + return conn, nil + }, + } +} + +// Close close the resource. +func (d *Dao) Close() error { + return d.redis.Close() +} + +// Ping dao ping. +func (d *Dao) Ping(c context.Context) error { + return d.pingRedis(c) +} diff --git a/logic/dao/kafka.go b/logic/dao/kafka.go new file mode 100644 index 0000000..910a189 --- /dev/null +++ b/logic/dao/kafka.go @@ -0,0 +1,44 @@ +package dao + +import ( + "context" + "fmt" + "github.com/opentracing/opentracing-go" + "gitlab.33.cn/chat/im-pkg/trace" + comet "gitlab.33.cn/chat/im/api/comet/grpc" + + "github.com/golang/protobuf/proto" + "github.com/rs/zerolog/log" + pb "gitlab.33.cn/chat/im/api/logic/grpc" + "gopkg.in/Shopify/sarama.v1" +) + +// PushMsg push a message to databus. +func (d *Dao) PublishMsg(ctx context.Context, appId string, fromId string, op comet.Op, key string, msg []byte) (err error) { + tracer := opentracing.GlobalTracer() + span, ctx := opentracing.StartSpanFromContextWithTracer(ctx, tracer, fmt.Sprintf("Publish -%v-%v", appId, op.String())) + defer span.Finish() + + pushMsg := &pb.BizMsg{ + AppId: appId, + FromId: fromId, + Op: int32(op), + Key: key, + Msg: msg, + } + b, err := proto.Marshal(pushMsg) + if err != nil { + return + } + appTopic := fmt.Sprintf("goim-%s-topic", appId) + m := &sarama.ProducerMessage{ + Key: sarama.StringEncoder(fromId), + Topic: appTopic, + Value: sarama.ByteEncoder(b), + } + trace.InjectMQHeader(tracer, span.Context(), ctx, m) + if _, _, err = d.kafkaPub.SendMessage(m); err != nil { + log.Error().Interface("pushMsg", pushMsg).Err(err).Msg("kafkaPub.SendMessage error") + } + return +} diff --git a/logic/dao/main_test.go b/logic/dao/main_test.go new file mode 100644 index 0000000..16e45f6 --- /dev/null +++ b/logic/dao/main_test.go @@ -0,0 +1,32 @@ +package dao + +import ( + "os" + "testing" + "time" + + xtime "github.com/Terry-Mao/goim/pkg/time" + "github.com/gomodule/redigo/redis" + "gitlab.33.cn/chat/im/logic/conf" +) + +var ( + testConf *conf.Config + testRedis *redis.Pool +) + +func TestMain(m *testing.M) { + testRedis = newRedis(&conf.Redis{ + Network: "tcp", + Addr: "127.0.0.1:6379", + Auth: "", + Active: 60000, + Idle: 1024, + DialTimeout: xtime.Duration(200 * time.Millisecond), + ReadTimeout: xtime.Duration(500 * time.Millisecond), + WriteTimeout: xtime.Duration(500 * time.Millisecond), + IdleTimeout: xtime.Duration(120 * time.Second), + Expire: xtime.Duration(30 * time.Minute), + }) + os.Exit(m.Run()) +} diff --git a/logic/dao/redis.go b/logic/dao/redis.go new file mode 100644 index 0000000..fabd29b --- /dev/null +++ b/logic/dao/redis.go @@ -0,0 +1,300 @@ +package dao + +import ( + "context" + "errors" + "fmt" + "strings" + + "github.com/gomodule/redigo/redis" + "github.com/rs/zerolog/log" +) + +const ( + MidFmt = "%s:%v" // {appId}:{uid} + _prefixMidServer = "mid_%s:%v" // mid_{appId}:{uid} -> key:server + _prefixKeyServer = "key_%s" // key_{key} -> server + _prefixKeyUser = "usr_%s" // usr_{key} -> {appId}:{uid} + _prefixGroupServer = "group_%s:%s" // group_{appId}:{gid} -> {server}:{score} +) + +func keyMidServer(appId string, mid string) string { + return fmt.Sprintf(_prefixMidServer, appId, mid) +} + +func keyKeyServer(key string) string { + return fmt.Sprintf(_prefixKeyServer, key) +} + +func keyKeyUser(key string) string { + return fmt.Sprintf(_prefixKeyUser, key) +} + +func keyGroupServer(appId, gid string) string { + return fmt.Sprintf(_prefixGroupServer, appId, gid) +} + +func (d *Dao) pingRedis(c context.Context) (err error) { + conn := d.redis.Get() + _, err = conn.Do("SET", "PING", "PONG") + conn.Close() + return +} + +func (d *Dao) GetMember(c context.Context, key string) (appId string, mid string, err error) { + conn := d.redis.Get() + defer conn.Close() + ss, err := redis.String(conn.Do("GET", keyKeyUser(key))) + if err != nil { + log.Error().Str("key", key).Err(err).Msg("conn.DO(GET)") + return "", "", err + } + arr := strings.Split(ss, ":") + if len(arr) != 2 { + return "", "", errors.New("invalid key") + } + appId = arr[0] + mid = arr[1] + return +} + +func (d *Dao) GetServer(c context.Context, key string) (server string, err error) { + conn := d.redis.Get() + defer conn.Close() + if server, err = redis.String(conn.Do("GET", keyKeyServer(key))); err != nil { + log.Error().Str("key", key).Err(err).Msg("conn.DO(GET)") + } + return +} + +func (d *Dao) AddMapping(c context.Context, mid string, appId string, key string, server string) (err error) { + conn := d.redis.Get() + defer conn.Close() + var n = 4 + if mid != "" { + if err = conn.Send("HSET", keyMidServer(appId, mid), key, server); err != nil { + log.Error().Str("key", key).Err(err).Msg(fmt.Sprintf("conn.Send(HSET %s,%s,%s,%s) error", appId, mid, server, key)) + return + } + if err = conn.Send("EXPIRE", keyMidServer(appId, mid), d.redisExpire); err != nil { + log.Error().Str("key", key).Err(err).Msg(fmt.Sprintf("conn.Send(EXPIRE %s,%s,%s,%s)", appId, mid, key, server)) + return + } + n += 2 + } + if err = conn.Send("SET", keyKeyServer(key), server); err != nil { + log.Error().Str("key", key).Err(err).Msg(fmt.Sprintf("conn.Send(HSET %s,%s,%s) error", mid, server, key)) + return + } + if err = conn.Send("EXPIRE", keyKeyServer(key), d.redisExpire); err != nil { + log.Error().Str("key", key).Err(err).Msg(fmt.Sprintf("conn.Send(EXPIRE %s,%s,%s) error", mid, key, server)) + return + } + user := fmt.Sprintf(MidFmt, appId, mid) + if err = conn.Send("SET", keyKeyUser(key), user); err != nil { + log.Error().Str("key", key).Err(err).Msg(fmt.Sprintf("conn.Send(HSET %s,%s,%s) error", mid, appId, key)) + return + } + if err = conn.Send("EXPIRE", keyKeyUser(key), d.redisExpire); err != nil { + log.Error().Str("key", key).Err(err).Msg(fmt.Sprintf("conn.Send(EXPIRE %s,%s,%s) error", mid, appId, key)) + return + } + if err = conn.Flush(); err != nil { + log.Error().Str("key", key).Err(err).Msg("conn.Flush() error") + return + } + for i := 0; i < n; i++ { + if _, err = conn.Receive(); err != nil { + log.Error().Str("key", key).Err(err).Msg("conn.Receive() error") + return + } + } + return +} + +// ExpireMapping expire a mapping. +func (d *Dao) ExpireMapping(c context.Context, mid string, appId string, key string) (has bool, err error) { + conn := d.redis.Get() + defer conn.Close() + var n = 2 + if mid != "" { + if err = conn.Send("EXPIRE", keyMidServer(appId, mid), d.redisExpire); err != nil { + log.Error().Str("key", key).Err(err).Msg(fmt.Sprintf("conn.Send(EXPIRE %s) error", keyMidServer(appId, mid))) + return + } + n++ + } + if err = conn.Send("EXPIRE", keyKeyServer(key), d.redisExpire); err != nil { + log.Error().Str("key", key).Err(err).Msg(fmt.Sprintf("conn.Send(EXPIRE %s) error", keyKeyServer(key))) + return + } + if err = conn.Send("EXPIRE", keyKeyUser(key), d.redisExpire); err != nil { + log.Error().Str("key", key).Err(err).Msg(fmt.Sprintf("conn.Send(EXPIRE %s) error", keyKeyServer(key))) + return + } + if err = conn.Flush(); err != nil { + log.Error().Str("key", key).Err(err).Msg("conn.Flush() error") + return + } + for i := 0; i < n; i++ { + if has, err = redis.Bool(conn.Receive()); err != nil { + log.Error().Str("key", key).Err(err).Msg("conn.Receive() error") + return + } + } + return +} + +func (d *Dao) DelMapping(c context.Context, mid string, appId string, key string) (has bool, err error) { + conn := d.redis.Get() + defer conn.Close() + var n = 2 + if mid != "" { + if err = conn.Send("HDEL", keyMidServer(appId, mid), key); err != nil { + log.Error().Str("key", key).Err(err).Msg(fmt.Sprintf("conn.Send(HDEL %s) error", keyMidServer(appId, mid))) + return + } + n++ + } + if err = conn.Send("DEL", keyKeyServer(key)); err != nil { + log.Error().Str("key", key).Err(err).Msg(fmt.Sprintf("conn.Send(DEL %s) error", keyKeyServer(key))) + return + } + if err = conn.Send("DEL", keyKeyUser(key)); err != nil { + log.Error().Str("key", key).Err(err).Msg(fmt.Sprintf("conn.Send(DEL %s) error", keyKeyUser(key))) + return + } + if err = conn.Flush(); err != nil { + log.Error().Str("key", key).Err(err).Msg("conn.Flush() error") + return + } + for i := 0; i < n; i++ { + if has, err = redis.Bool(conn.Receive()); err != nil { + log.Error().Str("key", key).Err(err).Msg("conn.Receive() error") + return + } + } + return +} + +// ServersByKeys get a server by key. +func (d *Dao) ServersByKeys(c context.Context, keys []string) (res []string, err error) { + conn := d.redis.Get() + defer conn.Close() + var args []interface{} + for _, key := range keys { + args = append(args, keyKeyServer(key)) + } + if res, err = redis.Strings(conn.Do("MGET", args...)); err != nil { + log.Error().Err(err).Msg(fmt.Sprintf("conn.Do(MGET %v) error", args)) + } + return +} + +// KeysByMids get a key server by mid. +func (d *Dao) KeysByMids(c context.Context, appId string, mids []string) (ress map[string]string, olMids []string, err error) { + conn := d.redis.Get() + defer conn.Close() + ress = make(map[string]string) + for _, mid := range mids { + if err = conn.Send("HGETALL", keyMidServer(appId, mid)); err != nil { + log.Error().Err(err).Msg(fmt.Sprintf("conn.Do(HGETALL %s) error", mid)) + return + } + } + if err = conn.Flush(); err != nil { + log.Error().Err(err).Msg("conn.Flush() error") + return + } + for idx := 0; idx < len(mids); idx++ { + var ( + res map[string]string + ) + if res, err = redis.StringMap(conn.Receive()); err != nil { + log.Error().Err(err).Msg("conn.Receive() error") + return + } + if len(res) > 0 { + olMids = append(olMids, mids[idx]) + } + for k, v := range res { + ress[k] = v + } + } + return +} + +//groups +func (d *Dao) IncGroupServer(c context.Context, appId, key, server string, gid []string) (err error) { + conn := d.redis.Get() + defer conn.Close() + var n = 0 + for _, g := range gid { + if err = conn.Send("ZINCRBY", keyGroupServer(appId, g), "1", server); err != nil { + log.Error().Str("key", key).Err(err).Msg( + fmt.Sprintf("conn.Send(ZINCRBY %s,%s,%s) error", keyGroupServer(appId, g), "1", server)) + return + } + //if err = conn.Send("EXPIRE", keyGroupServer(appId, g), d.redisExpire); err != nil { + // log.Error().Str("key", key).Err(err).Msg(fmt.Sprintf("conn.Send(EXPIRE %s,%s,%s,%s)", appId, mid, key, server)) + // return + //} + n++ + } + if err = conn.Flush(); err != nil { + log.Error().Str("key", key).Err(err).Msg("conn.Flush() error") + return + } + for i := 0; i < n; i++ { + if _, err = conn.Receive(); err != nil { + log.Error().Str("key", key).Err(err).Msg("conn.Receive() error") + return + } + } + return +} + +func (d *Dao) DecGroupServer(c context.Context, appId, key, server string, gid []string) (err error) { + conn := d.redis.Get() + defer conn.Close() + var n = 0 + for _, g := range gid { + if err = conn.Send("ZINCRBY", keyGroupServer(appId, g), "-1", server); err != nil { + log.Error().Str("key", key).Err(err).Msg( + fmt.Sprintf("conn.Send(ZINCRBY %s,%s,%s) error", keyGroupServer(appId, g), "-1", server)) + return + } + //if err = conn.Send("EXPIRE", keyGroupServer(appId, g), d.redisExpire); err != nil { + // log.Error().Str("key", key).Err(err).Msg(fmt.Sprintf("conn.Send(EXPIRE %s,%s,%s,%s)", appId, mid, key, server)) + // return + //} + n++ + } + if err = conn.Flush(); err != nil { + log.Error().Str("key", key).Err(err).Msg("conn.Flush() error") + return + } + for i := 0; i < n; i++ { + if _, err = conn.Receive(); err != nil { + log.Error().Str("key", key).Err(err).Msg("conn.Receive() error") + return + } + } + return +} + +// KeysByMids get a key server by mid. +func (d *Dao) ServersByGid(c context.Context, appId string, gid string) (res []string, err error) { + conn := d.redis.Get() + defer conn.Close() + res = make([]string, 0) + ress := make(map[string]string) + if ress, err = redis.StringMap(conn.Do("ZRANGE", keyGroupServer(appId, gid), "0", "-1", "WITHSCORES")); err != nil { + log.Error().Str("appId", appId).Str("gid", gid).Err(err).Msg( + fmt.Sprintf("conn.DO(ZRANGE %s,%s,%s,%s) error", keyGroupServer(appId, gid), "0", "-1", "WITHSCORES")) + } + for k, _ := range ress { + res = append(res, k) + } + return +} diff --git a/logic/dao/redis_test.go b/logic/dao/redis_test.go new file mode 100644 index 0000000..1f8a6bd --- /dev/null +++ b/logic/dao/redis_test.go @@ -0,0 +1,231 @@ +package dao + +import ( + "context" + "github.com/gomodule/redigo/redis" + "gopkg.in/Shopify/sarama.v1" + "testing" + + "gitlab.33.cn/chat/im/logic/conf" +) + +func TestDao_IncGroupServer(t *testing.T) { + type fields struct { + c *conf.Config + kafkaPub sarama.SyncProducer + redis *redis.Pool + redisExpire int32 + } + type args struct { + c context.Context + appId string + key string + server string + gid []string + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + // TODO: Add test cases. + { + name: "", + fields: fields{ + c: testConf, + kafkaPub: nil, + redis: testRedis, + redisExpire: 0, + }, + args: args{ + c: nil, + appId: "dtalk", + key: "1", + server: "grpc://172.0.0.1:8080", + gid: []string{"1"}, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + d := &Dao{ + c: tt.fields.c, + kafkaPub: tt.fields.kafkaPub, + redis: tt.fields.redis, + redisExpire: tt.fields.redisExpire, + } + if err := d.IncGroupServer(tt.args.c, tt.args.appId, tt.args.key, tt.args.server, tt.args.gid); (err != nil) != tt.wantErr { + t.Errorf("IncGroupServer() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestDao_DecGroupServer(t *testing.T) { + type fields struct { + c *conf.Config + kafkaPub sarama.SyncProducer + redis *redis.Pool + redisExpire int32 + } + type args struct { + c context.Context + appId string + key string + server string + gid []string + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + // TODO: Add test cases. + { + name: "", + fields: fields{ + c: testConf, + kafkaPub: nil, + redis: testRedis, + redisExpire: 0, + }, + args: args{ + c: nil, + appId: "dtalk", + key: "1", + server: "grpc://172.0.0.1:8080", + gid: []string{"1"}, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + d := &Dao{ + c: tt.fields.c, + kafkaPub: tt.fields.kafkaPub, + redis: tt.fields.redis, + redisExpire: tt.fields.redisExpire, + } + if err := d.DecGroupServer(tt.args.c, tt.args.appId, tt.args.key, tt.args.server, tt.args.gid); (err != nil) != tt.wantErr { + t.Errorf("IncGroupServer() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestDao_ServersByGid(t *testing.T) { + type fields struct { + c *conf.Config + kafkaPub sarama.SyncProducer + redis *redis.Pool + redisExpire int32 + } + type args struct { + c context.Context + appId string + gid string + } + tests := []struct { + name string + fields fields + args args + wantRes []string + wantErr bool + }{ + // TODO: Add test cases. + { + name: "", + fields: fields{ + c: nil, + kafkaPub: nil, + redis: testRedis, + redisExpire: 0, + }, + args: args{ + c: nil, + appId: "dtalk", + gid: "1", + }, + wantRes: nil, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + d := &Dao{ + c: tt.fields.c, + kafkaPub: tt.fields.kafkaPub, + redis: tt.fields.redis, + redisExpire: tt.fields.redisExpire, + } + gotRes, err := d.ServersByGid(tt.args.c, tt.args.appId, tt.args.gid) + if (err != nil) != tt.wantErr { + t.Errorf("ServersByGid() error = %v, wantErr %v", err, tt.wantErr) + return + } + for i, re := range gotRes { + t.Logf("got %v:%v\n", i, re) + } + }) + } +} + +func TestDao_KeysByMids(t *testing.T) { + type fields struct { + c *conf.Config + kafkaPub sarama.SyncProducer + redis *redis.Pool + redisExpire int32 + } + type args struct { + c context.Context + appId string + mids []string + } + tests := []struct { + name string + fields fields + args args + wantRess map[string]string + wantOlMids []string + wantErr bool + }{ + { + name: "", + fields: fields{ + c: testConf, + kafkaPub: nil, + redis: testRedis, + redisExpire: 0, + }, + args: args{ + c: context.Background(), + appId: "dtalk", + mids: []string{"1ygj6Un2UzL2rev6ub6NukWrGcKjW8LoG", "1LNaxM1BtkkRpWEGty8bDxmvWwRwxsCy1B", "14si8HGSBKN2B4Ps7QQJeRLvqWoXHX2NwB", ""}, + }, + wantRess: nil, + wantOlMids: nil, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + d := &Dao{ + c: tt.fields.c, + kafkaPub: tt.fields.kafkaPub, + redis: tt.fields.redis, + redisExpire: tt.fields.redisExpire, + } + gotRess, gotOlMids, err := d.KeysByMids(tt.args.c, tt.args.appId, tt.args.mids) + if err != nil { + t.Error(err) + } + t.Log(gotRess) + t.Log(gotOlMids) + }) + } +} diff --git a/logic/grpc/server.go b/logic/grpc/server.go new file mode 100644 index 0000000..bbb3d64 --- /dev/null +++ b/logic/grpc/server.go @@ -0,0 +1,187 @@ +package grpc + +import ( + "context" + "net" + "time" + + "github.com/golang/protobuf/proto" + "gitlab.33.cn/chat/im-pkg/trace" + pb "gitlab.33.cn/chat/im/api/logic/grpc" + "gitlab.33.cn/chat/im/logic" + "gitlab.33.cn/chat/im/logic/conf" + "google.golang.org/grpc" + _ "google.golang.org/grpc/encoding/gzip" + "google.golang.org/grpc/keepalive" +) + +func New(c *conf.RPCServer, l *logic.Logic) *grpc.Server { + keepParams := grpc.KeepaliveParams(keepalive.ServerParameters{ + MaxConnectionIdle: time.Duration(c.IdleTimeout), + MaxConnectionAgeGrace: time.Duration(c.ForceCloseWait), + Time: time.Duration(c.KeepAliveInterval), + Timeout: time.Duration(c.KeepAliveTimeout), + MaxConnectionAge: time.Duration(c.MaxLifeTime), + }) + connectionTimeout := grpc.ConnectionTimeout(time.Duration(c.Timeout)) + srv := grpc.NewServer(keepParams, connectionTimeout, + grpc.ChainUnaryInterceptor( + trace.OpentracingServerInterceptor, + )) + pb.RegisterLogicServer(srv, &server{srv: l}) + lis, err := net.Listen(c.Network, c.Addr) + if err != nil { + panic(err) + } + go func() { + if err := srv.Serve(lis); err != nil { + panic(err) + } + }() + return srv +} + +type server struct { + pb.UnimplementedLogicServer + srv *logic.Logic +} + +var _ pb.LogicServer = &server{} + +// Connect connect a conn. +func (s *server) Connect(ctx context.Context, req *pb.ConnectReq) (*pb.ConnectReply, error) { + mid, appId, key, hb, err := s.srv.Connect(ctx, req.Server, req.Proto) + if err != nil { + return nil, err + } + return &pb.ConnectReply{Key: key, AppId: appId, Mid: mid, Heartbeat: hb}, nil +} + +// Disconnect disconnect a conn. +func (s *server) Disconnect(ctx context.Context, req *pb.DisconnectReq) (*pb.Reply, error) { + _, err := s.srv.Disconnect(ctx, req.Key, req.Server) + if err != nil { + return nil, err + } + return &pb.Reply{IsOk: true}, nil +} + +// Heartbeat beartbeat a conn. +func (s *server) Heartbeat(ctx context.Context, req *pb.HeartbeatReq) (*pb.Reply, error) { + if err := s.srv.Heartbeat(ctx, req.Key, req.Server); err != nil { + return nil, err + } + return &pb.Reply{IsOk: true}, nil +} + +// Receive receive a message from client. +func (s *server) Receive(ctx context.Context, req *pb.ReceiveReq) (*pb.Reply, error) { + if err := s.srv.Receive(ctx, req.Key, req.Proto); err != nil { + return nil, err + } + return &pb.Reply{IsOk: true}, nil +} + +// Push push a biz message to client. +func (s *server) PushByMids(ctx context.Context, req *pb.MidsMsg) (*pb.Reply, error) { + reply, err := s.srv.PushByMids(ctx, req.AppId, req.ToIds, req.Msg) + if err != nil { + return nil, err + } + msg, err := proto.Marshal(reply) + if err != nil { + return nil, err + } + return &pb.Reply{IsOk: true, Msg: msg}, nil +} + +// Push push a biz message to client. +func (s *server) PushByKeys(ctx context.Context, req *pb.KeysMsg) (*pb.Reply, error) { + reply, err := s.srv.PushByKeys(ctx, req.AppId, req.ToKeys, req.Msg) + if err != nil { + return nil, err + } + msg, err := proto.Marshal(reply) + if err != nil { + return nil, err + } + return &pb.Reply{IsOk: true, Msg: msg}, nil +} + +// Push push a biz message to client. +func (s *server) PushGroup(ctx context.Context, req *pb.GroupMsg) (*pb.Reply, error) { + reply, err := s.srv.PushGroup(ctx, req.AppId, req.Group, req.Msg) + if err != nil { + return nil, err + } + msg, err := proto.Marshal(reply) + if err != nil { + return nil, err + } + return &pb.Reply{IsOk: true, Msg: msg}, nil +} + +// Push push a biz message to client. +func (s *server) JoinGroupsByKeys(ctx context.Context, req *pb.GroupsKey) (*pb.Reply, error) { + reply, err := s.srv.JoinGroupsByKeys(ctx, req.AppId, req.Keys, req.Gid) + if err != nil { + return nil, err + } + msg, err := proto.Marshal(reply) + if err != nil { + return nil, err + } + return &pb.Reply{IsOk: true, Msg: msg}, nil +} + +// Push push a biz message to client. +func (s *server) JoinGroupsByMids(ctx context.Context, req *pb.GroupsMid) (*pb.Reply, error) { + reply, err := s.srv.JoinGroupsByMids(ctx, req.AppId, req.Mids, req.Gid) + if err != nil { + return nil, err + } + msg, err := proto.Marshal(reply) + if err != nil { + return nil, err + } + return &pb.Reply{IsOk: true, Msg: msg}, nil +} + +// Push push a biz message to client. +func (s *server) LeaveGroupsByKeys(ctx context.Context, req *pb.GroupsKey) (*pb.Reply, error) { + reply, err := s.srv.LeaveGroupsByKeys(ctx, req.AppId, req.Keys, req.Gid) + if err != nil { + return nil, err + } + msg, err := proto.Marshal(reply) + if err != nil { + return nil, err + } + return &pb.Reply{IsOk: true, Msg: msg}, nil +} + +// Push push a biz message to client. +func (s *server) LeaveGroupsByMids(ctx context.Context, req *pb.GroupsMid) (*pb.Reply, error) { + reply, err := s.srv.LeaveGroupsByMids(ctx, req.AppId, req.Mids, req.Gid) + if err != nil { + return nil, err + } + msg, err := proto.Marshal(reply) + if err != nil { + return nil, err + } + return &pb.Reply{IsOk: true, Msg: msg}, nil +} + +// Push push a biz message to client. +func (s *server) DelGroups(ctx context.Context, req *pb.DelGroupsReq) (*pb.Reply, error) { + reply, err := s.srv.DelGroups(ctx, req.AppId, req.Gid) + if err != nil { + return nil, err + } + msg, err := proto.Marshal(reply) + if err != nil { + return nil, err + } + return &pb.Reply{IsOk: true, Msg: msg}, nil +} diff --git a/logic/logic.go b/logic/logic.go new file mode 100644 index 0000000..12854d3 --- /dev/null +++ b/logic/logic.go @@ -0,0 +1,426 @@ +package logic + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/golang/protobuf/proto" + "github.com/google/uuid" + "github.com/rs/zerolog/log" + "gitlab.33.cn/chat/im-pkg/trace" + comet "gitlab.33.cn/chat/im/api/comet/grpc" + "gitlab.33.cn/chat/im/common" + "gitlab.33.cn/chat/im/logic/auth" + "gitlab.33.cn/chat/im/logic/conf" + "gitlab.33.cn/chat/im/logic/dao" + "gitlab.33.cn/chat/im/naming" + xkey "gitlab.33.cn/chat/im/naming/balancer/key" + "google.golang.org/grpc" + "google.golang.org/grpc/resolver" +) + +var ErrInvalidAuthReq = errors.New("ErrInvalidAuthReq") +var ErrInvalidAppId = errors.New("ErrInvalidAppId") + +type Logic struct { + c *conf.Config + dao *dao.Dao + cometClient comet.CometClient + apps map[string]auth.Auth +} + +func New(c *conf.Config) (l *Logic) { + cometClient, err := newCometClient(c) + if err != nil { + panic(err) + } + + l = &Logic{ + c: c, + dao: dao.New(c), + cometClient: cometClient, + apps: make(map[string]auth.Auth), + } + l.loadApps() + return l +} + +func newCometClient(c *conf.Config) (comet.CometClient, error) { + rb := naming.NewResolver(c.Reg.RegAddrs, c.CometRPCClient.Schema) + resolver.Register(rb) + + addr := fmt.Sprintf("%s:///%s", c.CometRPCClient.Schema, c.CometRPCClient.SrvName) // "schema://[authority]/service" + fmt.Println("rpc client call addr:", addr) + conn, err := common.NewGRPCConnWithKey(addr, time.Duration(c.CometRPCClient.Dial), grpc.WithUnaryInterceptor(trace.OpentracingClientInterceptor)) + if err != nil { + panic(err) + } + return comet.NewCometClient(conn), err +} + +func (l *Logic) loadApps() { + for _, app := range l.c.Apps { + newAuth, _ := auth.Load(app.AppId) + if newAuth == nil { + panic("exec auth not exist:" + app.AppId) + } + exec := newAuth(app.AuthUrl, time.Duration(app.Timeout)) + l.apps[app.AppId] = exec + } +} + +func (l *Logic) Close() { + l.dao.Close() +} + +// Connect connected a conn. +func (l *Logic) Connect(c context.Context, server string, p *comet.Proto) (mid string, appId string, key string, hb int64, err error) { + var ( + authMsg comet.AuthMsg + bytes []byte + ) + log.Info().Str("appId", authMsg.AppId).Bytes("body", p.Body).Str("token", authMsg.Token).Msg("call Connect") + err = proto.Unmarshal(p.Body, &authMsg) + if err != nil { + return + } + + if authMsg.AppId == "" || authMsg.Token == "" { + err = ErrInvalidAuthReq + return + } + log.Info().Str("appId", authMsg.AppId).Str("token", authMsg.Token).Msg("call auth") + appId = authMsg.AppId + authExec, _ := l.apps[authMsg.AppId] + if authExec == nil { + err = ErrInvalidAppId + return + } + + mid, err = authExec.DoAuth(authMsg.Token) + if err != nil { + return + } + + hb = int64(l.c.Node.Heartbeat) * int64(l.c.Node.HeartbeatMax) + key = uuid.New().String() //连接标识 + if err = l.dao.AddMapping(c, mid, appId, key, server); err != nil { + log.Error().Err(err).Msg(fmt.Sprintf("l.dao.AddMapping(%s,%s,%s) error", mid, key, server)) + return + } + log.Info().Str("key", key).Str("mid", mid).Str("appId", appId).Str("comet", server).Msg("conn connected") + // notify biz user connected + bytes, err = proto.Marshal(p) + if err != nil { + return + } + err = l.dao.PublishMsg(c, appId, mid, comet.Op_Auth, key, bytes) + return +} + +// Disconnect disconnect a conn. +func (l *Logic) Disconnect(c context.Context, key string, server string) (has bool, err error) { + appId, mid, err := l.dao.GetMember(c, key) + if err != nil { + return false, err + } + if has, err = l.dao.DelMapping(c, mid, appId, key); err != nil { + log.Error().Err(err).Msg(fmt.Sprintf("l.dao.DelMapping(%s,%s) error", mid, appId)) + return + } + log.Info().Str("key", key).Str("mid", mid).Str("appId", appId).Str("comet", server).Msg("conn disconnected") + // notify biz user disconnected + l.dao.PublishMsg(c, appId, mid, comet.Op_Disconnect, key, nil) + return +} + +// Heartbeat heartbeat a conn. +func (l *Logic) Heartbeat(c context.Context, key string, server string) (err error) { + appId, mid, err := l.dao.GetMember(c, key) + if err != nil { + return err + } + + var has bool + has, err = l.dao.ExpireMapping(c, mid, appId, key) + if err != nil { + log.Error().Err(err).Msg(fmt.Sprintf("l.dao.ExpireMapping(%s,%s) error", mid, appId)) + return + } + if !has { + if err = l.dao.AddMapping(c, mid, appId, key, server); err != nil { + log.Error().Err(err).Msg(fmt.Sprintf("l.dao.AddMapping(%s,%s,%s) error", mid, appId, server)) + return + } + } + log.Debug().Str("key", key).Str("mid", mid).Str("appId", appId).Str("comet", server).Msg("conn heartbeat") + return +} + +// Receive receive a message from client. +func (l *Logic) Receive(c context.Context, key string, p *comet.Proto) (err error) { + appId, mid, err := l.dao.GetMember(c, key) + if err != nil { + return err + } + + log.Debug().Str("appId", appId).Str("mid", mid).Interface("proto", p).Msg("receive proto") + msg, err := proto.Marshal(p) + if err != nil { + return err + } + return l.dao.PublishMsg(c, appId, mid, comet.Op(p.Op), key, msg) +} + +// PushByMids Push push a biz message to client. +func (l *Logic) PushByMids(c context.Context, appId string, toIds []string, msg []byte) (reply *comet.PushMsgReply, err error) { + log.Debug().Str("appId", appId).Strs("mids", toIds).Msg("PushByMids start") + + var p comet.Proto + err = proto.Unmarshal(msg, &p) + if err != nil { + return + } + + server2keys, err := l.getServerByMids(c, appId, toIds) + if err != nil { + return + } + + for server, keys := range server2keys { + log.Debug().Strs("keys", keys).Str("server", server).Msg("PushByMids pushing") + reply, err = l.cometClient.PushMsg(context.WithValue(c, xkey.DefaultKey, convertAddress(server)), &comet.PushMsgReq{Keys: keys, Proto: &p}) + if err != nil { + log.Error().Err(err).Strs("keys", keys).Str("server", server).Msg("PushByMids l.cometClient.PushMsg") + } + } + + return +} + +// PushByKeys Push push a biz message to client. +func (l *Logic) PushByKeys(c context.Context, appId string, keys []string, msg []byte) (reply *comet.PushMsgReply, err error) { + log.Debug().Str("appId", appId).Strs("keys", keys).Msg("PushByKeys start") + + var p comet.Proto + err = proto.Unmarshal(msg, &p) + if err != nil { + return + } + + server2keys, err := l.getServerByKeys(c, keys) + if err != nil { + return + } + + for server, keys := range server2keys { + log.Debug().Strs("keys", keys).Str("server", server).Msg("PushByMids pushing") + reply, err = l.cometClient.PushMsg(context.WithValue(c, xkey.DefaultKey, convertAddress(server)), &comet.PushMsgReq{Keys: keys, Proto: &p}) + if err != nil { + log.Error().Err(err).Strs("keys", keys).Str("server", server).Msg("PushByMids l.cometClient.PushMsg") + } + } + + return +} + +// PushGroup Push push a biz message to client. +func (l *Logic) PushGroup(c context.Context, appId string, group string, msg []byte) (reply *comet.BroadcastGroupReply, err error) { + log.Debug().Str("appId", appId).Str("group", group).Msg("PushGroup start") + var p comet.Proto + err = proto.Unmarshal(msg, &p) + if err != nil { + return + } + + cometServers, err := l.dao.ServersByGid(c, appId, group) + if err != nil { + return + } + + for _, server := range cometServers { + log.Debug().Str("appId", appId).Str("group", group).Str("server", server).Msg("PushGroup pushing") + reply, err = l.cometClient.BroadcastGroup(context.WithValue(c, xkey.DefaultKey, convertAddress(server)), &comet.BroadcastGroupReq{GroupID: appGid(appId, group), Proto: &p}) + if err != nil { + log.Error().Err(err).Str("appId", appId).Str("group", group).Str("server", server).Msg("PushGroup l.cometClient.BroadcastGroup") + } + } + + return +} + +// JoinGroupsByMids Push push a biz message to client. +func (l *Logic) JoinGroupsByMids(c context.Context, appId string, mids []string, gids []string) (reply *comet.JoinGroupsReply, err error) { + log.Debug().Str("appId", appId).Strs("mids", mids).Strs("group", gids).Msg("JoinGroupsByMids start") + + server2keys, err := l.getServerByMids(c, appId, mids) + if err != nil { + return + } + + for server, keys := range server2keys { + for _, key := range keys { + _ = l.dao.IncGroupServer(c, appId, key, server, gids) + } + + log.Debug().Strs("keys", keys).Str("server", server).Msg("JoinGroupsByMids pushing") + reply, err = l.cometClient.JoinGroups(context.WithValue(c, xkey.DefaultKey, convertAddress(server)), &comet.JoinGroupsReq{Keys: keys, Gid: appGids(appId, gids)}) + if err != nil { + log.Error().Err(err).Strs("keys", keys).Str("server", server).Msg("JoinGroupsByMids l.cometClient.JoinGroups") + } + } + + return +} + +// JoinGroupsByKeys Push push a biz message to client. +func (l *Logic) JoinGroupsByKeys(c context.Context, appId string, keys []string, gids []string) (reply *comet.JoinGroupsReply, err error) { + log.Debug().Str("appId", appId).Strs("keys", keys).Strs("group", gids).Msg("JoinGroupsByKeys start") + + server2keys, err := l.getServerByKeys(c, keys) + if err != nil { + return + } + + for server, keys := range server2keys { + for _, key := range keys { + _ = l.dao.IncGroupServer(c, appId, key, server, gids) + } + + log.Debug().Strs("keys", keys).Str("server", server).Msg("JoinGroupsByKeys pushing") + reply, err = l.cometClient.JoinGroups(context.WithValue(c, xkey.DefaultKey, convertAddress(server)), &comet.JoinGroupsReq{Keys: keys, Gid: appGids(appId, gids)}) + if err != nil { + log.Error().Err(err).Strs("keys", keys).Str("server", server).Msg("JoinGroupsByKeys l.cometClient.JoinGroups") + } + } + + return +} + +// LeaveGroupsByMids Push push a biz message to client. +func (l *Logic) LeaveGroupsByMids(c context.Context, appId string, mids []string, gids []string) (reply *comet.LeaveGroupsReply, err error) { + log.Debug().Str("appId", appId).Strs("mids", mids).Strs("group", gids).Msg("LeaveGroupsByMids start") + + server2keys, err := l.getServerByMids(c, appId, mids) + if err != nil { + return + } + + for server, keys := range server2keys { + for _, key := range keys { + _ = l.dao.DecGroupServer(c, appId, key, server, gids) + } + + log.Debug().Strs("keys", keys).Str("server", server).Msg("LeaveGroupsByMids pushing") + reply, err = l.cometClient.LeaveGroups(context.WithValue(c, xkey.DefaultKey, convertAddress(server)), &comet.LeaveGroupsReq{Keys: keys, Gid: appGids(appId, gids)}) + if err != nil { + log.Error().Err(err).Strs("keys", keys).Str("server", server).Msg("LeaveGroupsByMids l.cometClient.LeaveGroups") + } + } + + return +} + +// LeaveGroupsByKeys Push push a biz message to client. +func (l *Logic) LeaveGroupsByKeys(c context.Context, appId string, keys []string, gids []string) (reply *comet.LeaveGroupsReply, err error) { + log.Debug().Str("appId", appId).Strs("keys", keys).Strs("group", gids).Msg("LeaveGroupsByKeys start") + + server2keys, err := l.getServerByKeys(c, keys) + if err != nil { + return + } + + for server, keys := range server2keys { + for _, key := range keys { + _ = l.dao.DecGroupServer(c, appId, key, server, gids) + } + + log.Debug().Strs("keys", keys).Str("server", server).Msg("LeaveGroupsByKeys pushing") + reply, err = l.cometClient.LeaveGroups(context.WithValue(c, xkey.DefaultKey, convertAddress(server)), &comet.LeaveGroupsReq{Keys: keys, Gid: appGids(appId, gids)}) + if err != nil { + log.Error().Err(err).Strs("keys", keys).Str("server", server).Msg("LeaveGroupsByKeys l.cometClient.LeaveGroups") + } + } + + return +} + +// DelGroups Push push a biz message to client. +func (l *Logic) DelGroups(c context.Context, appId string, gids []string) (reply *comet.DelGroupsReply, err error) { + log.Debug().Str("appId", appId).Strs("groups", gids).Msg("DelGroups start") + + server2gids := make(map[string][]string) + + for _, gid := range gids { + cometServers, err := l.dao.ServersByGid(c, appId, gid) + if err != nil { + continue + } + + for _, server := range cometServers { + server2gids[server] = append(server2gids[server], gid) + } + } + + for server, gids := range server2gids { + log.Debug().Str("appId", appId).Interface("gids", gids).Str("server", server).Msg("DelGroups pushing") + reply, err = l.cometClient.DelGroups(context.WithValue(c, xkey.DefaultKey, convertAddress(server)), &comet.DelGroupsReq{Gid: appGids(appId, gids)}) + if err != nil { + log.Error().Err(err).Str("appId", appId).Strs("groups", gids).Str("server", server).Msg("DelGroups l.cometClient.DelGroups") + } + } + + return +} + +// getServerByMids 通过 appid 和 mids 找到所在的 comet servers +func (l *Logic) getServerByMids(c context.Context, appId string, mids []string) (map[string][]string, error) { + ress, _, err := l.dao.KeysByMids(c, appId, mids) + if err != nil { + return nil, err + } + + server2keys := make(map[string][]string) + for k, s := range ress { + server2keys[s] = append(server2keys[s], k) + } + + return server2keys, nil +} + +// getServerByKeys 通过 keys 找到所在的 comet servers +func (l *Logic) getServerByKeys(c context.Context, keys []string) (map[string][]string, error) { + server2keys := make(map[string][]string) + for _, key := range keys { + server, _ := l.dao.GetServer(c, key) + server2keys[server] = append(server2keys[server], key) + } + return server2keys, nil +} + +func appGid(appId, gid string) string { + return fmt.Sprintf("%s_%s", appId, gid) +} + +func appGids(appId string, gids []string) []string { + res := make([]string, 0, len(gids)) + for _, g := range gids { + res = append(res, appGid(appId, g)) + } + return res +} + +func convertAddress(addr string) string { + // todo + if len(addr) == 0 { + return addr + } + switch addr[0] { + case 'g': + return addr[7:] + default: + return addr + } +} diff --git a/naming/balancer/key/key.go b/naming/balancer/key/key.go new file mode 100644 index 0000000..0e5433f --- /dev/null +++ b/naming/balancer/key/key.go @@ -0,0 +1,88 @@ +package key + +import ( + "github.com/rs/zerolog/log" + "google.golang.org/grpc/balancer" + "google.golang.org/grpc/balancer/base" + "google.golang.org/grpc/grpclog" + "sync" +) + +const ( + Name = "picker_key" + DefaultKey = "X-rand-string-balabala" +) + +func init() { + balancer.Register(newBuilder(DefaultKey)) +} + +func newBuilder(key string) balancer.Builder { + return base.NewBalancerBuilder( + Name, + &keyPickerBuilder{key: key}, + base.Config{HealthCheck: false}, + ) +} + +type keyPickerBuilder struct { + key string +} + +func (b *keyPickerBuilder) Build(info base.PickerBuildInfo) balancer.Picker { + grpclog.Infof("keyPickerBuilder: newPicker called with info: %v", info) + if len(info.ReadySCs) == 0 { + return base.NewErrPicker(balancer.ErrNoSubConnAvailable) + } + + picker := &keyPicker{ + addr2sc: make(map[string]balancer.SubConn), + key: b.key, + } + + for sc, conInfo := range info.ReadySCs { + addr := conInfo.Address.Addr + picker.setAddr2sc(addr, sc) + } + + return picker +} + +type keyPicker struct { + addr2sc map[string]balancer.SubConn + sync.RWMutex + key string +} + +func (p *keyPicker) setAddr2sc(addr string, sc balancer.SubConn) { + p.Lock() + defer p.Unlock() + p.addr2sc[addr] = sc + log.Debug().Str("addr", addr).Msg("set addr 2 sc") +} + +func (p *keyPicker) getAddr2sc(addr string) (balancer.SubConn, error) { + p.RLock() + defer p.RUnlock() + sc, ok := p.addr2sc[addr] + if ok { + return sc, nil + } + log.Warn().Str("addr", addr).Msg("server warn") + // TODO 如果找不到就随便给一个 + for _, tsc := range p.addr2sc { + + return tsc, nil + } + return nil, nil +} + +func (p *keyPicker) Pick(info balancer.PickInfo) (balancer.PickResult, error) { + var res balancer.PickResult + + addr, _ := info.Ctx.Value(p.key).(string) + + sc, _ := p.getAddr2sc(addr) + res.SubConn = sc + return res, nil +} diff --git a/naming/register.go b/naming/register.go new file mode 100644 index 0000000..fdf7458 --- /dev/null +++ b/naming/register.go @@ -0,0 +1,86 @@ +package naming + +import ( + "context" + "fmt" + "log" + "strings" + "time" + + "go.etcd.io/etcd/client/v3" +) + +func Register(etcdAddr, name, addr, schema string, ttl int64) error { + var err error + if cli == nil { + cli, err = clientv3.New(clientv3.Config{ + Endpoints: strings.Split(etcdAddr, ";"), + DialTimeout: 5 * time.Second, + }) + if err != nil { + log.Printf("connect to etcd err:%s", err) + return err + } + } + + ticker := time.NewTicker(time.Second * time.Duration(ttl)) + + go func() { + for { + getResp, err := cli.Get(context.Background(), "/"+schema+"/"+name+"/"+addr) + if err != nil { + fmt.Println("etcd get err:", err) + } else if getResp.Count == 0 { + err = withAlive(name, addr, schema, ttl) + if err != nil { + log.Printf("keep alive:%s", err) + } + } + <-ticker.C + } + }() + + return nil +} + +func withAlive(name, addr, schema string, ttl int64) error { + leaseResp, err := cli.Grant(context.Background(), ttl) + if err != nil { + return err + } + + log.Printf("key:%v\n", "/"+schema+"/"+name+"/"+addr) + _, err = cli.Put(context.Background(), "/"+schema+"/"+name+"/"+addr, addr, clientv3.WithLease(leaseResp.ID)) + if err != nil { + log.Printf("put etcd error:%s", err) + return err + } + + ch, err := cli.KeepAlive(context.Background(), leaseResp.ID) + if err != nil { + log.Printf("keep alive error:%s", err) + return err + } + + go func() { + for range ch { + } + //for leaseKeepResp := range ch { + // log.Println("续约成功", leaseKeepResp.ID) + //} + // + //log.Println("关闭续租") + }() + + return nil +} + +func UnRegister(name, addr, schema string) error { + if cli != nil { + //fmt.Println("unregister...") + _, err := cli.Delete(context.Background(), "/"+schema+"/"+name+"/"+addr) + return err + } + + return nil +} diff --git a/naming/resolver.go b/naming/resolver.go new file mode 100644 index 0000000..fc5f922 --- /dev/null +++ b/naming/resolver.go @@ -0,0 +1,112 @@ +package naming + +import ( + "context" + "log" + "strings" + "time" + + "go.etcd.io/etcd/api/v3/mvccpb" + "go.etcd.io/etcd/client/v3" + "google.golang.org/grpc/resolver" +) + +var schema string +var cli *clientv3.Client + +type etcdResolver struct { + rawAddr string + schema string + cc resolver.ClientConn +} + +func NewResolver(etcdAddr, schema string) resolver.Builder { + return &etcdResolver{rawAddr: etcdAddr, schema: schema} +} + +func (r *etcdResolver) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) { + //fmt.Println("target:", target) + var err error + if cli == nil { + cli, err = clientv3.New(clientv3.Config{ + Endpoints: strings.Split(r.rawAddr, ";"), + DialTimeout: 15 * time.Second, + }) + if err != nil { + return nil, err + } + } + + r.cc = cc + + go r.watch("/" + target.Scheme + "/" + target.Endpoint + "/") + + return r, nil +} + +func (r etcdResolver) Scheme() string { + return r.schema +} + +func (r etcdResolver) ResolveNow(rn resolver.ResolveNowOptions) { + //log.Println("ResolveNow") +} + +func (r etcdResolver) Close() { + //log.Println("Close") +} + +func (r *etcdResolver) watch(keyPrefix string) { + var addrList []resolver.Address + + getResp, err := cli.Get(context.Background(), keyPrefix, clientv3.WithPrefix()) + if err != nil { + log.Println(err) + } else { + for i := range getResp.Kvs { + addrList = append(addrList, resolver.Address{Addr: strings.TrimPrefix(string(getResp.Kvs[i].Key), keyPrefix)}) + } + } + + // 新版本etcd去除了NewAddress方法 以UpdateState代替 + r.cc.UpdateState(resolver.State{Addresses: addrList}) + + rch := cli.Watch(context.Background(), keyPrefix, clientv3.WithPrefix()) + for n := range rch { + for _, ev := range n.Events { + addr := strings.TrimPrefix(string(ev.Kv.Key), keyPrefix) + switch ev.Type { + case mvccpb.PUT: + if !exist(addrList, addr) { + addrList = append(addrList, resolver.Address{Addr: addr}) + r.cc.UpdateState(resolver.State{Addresses: addrList}) + } + case mvccpb.DELETE: + if s, ok := remove(addrList, addr); ok { + addrList = s + r.cc.UpdateState(resolver.State{Addresses: addrList}) + } + } + log.Printf("%s %q : %q\n", ev.Type, ev.Kv.Key, ev.Kv.Value) + } + } +} + +func exist(l []resolver.Address, addr string) bool { + for i := range l { + if l[i].Addr == addr { + return true + } + } + return false +} + +func remove(s []resolver.Address, addr string) ([]resolver.Address, bool) { + for i := range s { + if s[i].Addr == addr { + s[i] = s[len(s)-1] + return s[:len(s)-1], true + } + } + return nil, false +} diff --git a/script/build/quick_build.sh b/script/build/quick_build.sh new file mode 100644 index 0000000..efad91d --- /dev/null +++ b/script/build/quick_build.sh @@ -0,0 +1,43 @@ +#!/bin/bash +file_path=$( + cd "$(dirname "$0")" || exit + pwd +)/../.. +# shellcheck source=./util.sh +source "${file_path}"/script/build/util.sh + +quickBuildService() { + file_path="$1" + pkg_name="$2" + service_name="$3" + flags=$(getFlags) + + cd "${file_path}/${service_name}/cmd" || exit + echo "┌ start building ${service_name} service" + go build -ldflags "${flags}" -o "${file_path}/${pkg_name}/${service_name}" || exit + echo "└ building ${service_name} service success" +} + +mkDir() { + file_path="$1" + pkg_path="$2" + + rm -rf ${file_path}/${pkg_path} + mkdir -pv "${file_path}/${pkg_path}" +} + +env_type="$1" +initOS ${env_type} +os=$(initOS ${env_type}) + +api_version="" +project_name="im" +now_time=$(date "+%Y_%m_%d") +pkg_path="${project_name}_bin_${now_time}_${os}" + +mkDir "${file_path}" "${pkg_path}" + +quickBuildService "${file_path}" "${pkg_path}" "comet" +quickBuildService "${file_path}" "${pkg_path}" "logic" + + diff --git a/script/build/util.sh b/script/build/util.sh new file mode 100644 index 0000000..4dae1e7 --- /dev/null +++ b/script/build/util.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# 获取编译参数 +getFlags() { + main_path="main" + go_version=$(go version | awk '{ print $3 }') + build_time=$(date "+%Y-%m-%d %H:%M:%S %Z") + git_commit=$(git rev-parse --short=10 HEAD) + flags="-X '${main_path}.goVersion=${go_version}' -X '${main_path}.buildTime=${build_time}' -X '${main_path}.gitCommit=${git_commit}' -X 'google.golang.org/protobuf/reflect/protoregistry.conflictPolicy=warn'" + echo "${flags}" +} + +# 设置目标打包环境 +# 默认 linux amd64 +initOS() { + env_type="amd64" + if [ -n "$1" ]; then + env_type="$1" + fi + + export GOOS=linux + export GOARCH=${env_type} + export GOLANG_PROTOBUF_REGISTRATION_CONFLICT=warn + + echo "linux_${env_type}" +} \ No newline at end of file diff --git a/script/gen-proto/protoc-gen-go-grpc_v1.1.0 b/script/gen-proto/protoc-gen-go-grpc_v1.1.0 new file mode 100644 index 0000000..97300ab Binary files /dev/null and b/script/gen-proto/protoc-gen-go-grpc_v1.1.0 differ diff --git a/script/gen-proto/protoc-gen-go_v1.27.1 b/script/gen-proto/protoc-gen-go_v1.27.1 new file mode 100644 index 0000000..5ed12cd Binary files /dev/null and b/script/gen-proto/protoc-gen-go_v1.27.1 differ diff --git a/script/gen-proto/protoc_v3.19.1 b/script/gen-proto/protoc_v3.19.1 new file mode 100644 index 0000000..bcf0afd Binary files /dev/null and b/script/gen-proto/protoc_v3.19.1 differ diff --git a/script/im.lua b/script/im.lua new file mode 100644 index 0000000..ec071a0 --- /dev/null +++ b/script/im.lua @@ -0,0 +1,82 @@ +local name = "im" +local p_im = Proto(name, "im layer protocal"); + +local f_pack_size = ProtoField.uint32(name .. ".packsize", "Packet Size", base.DEC) +local f_header_len = ProtoField.uint16(name .. ".header", "Header Length", base.DEC) +local f_version = ProtoField.uint16(name .. ".version", "Version", base.DEC) +local f_option = ProtoField.uint32(name .. ".option", "Option", base.DEC) +local f_seq = ProtoField.uint32(name .. ".seq", "Seq", base.DEC) +local f_ack = ProtoField.uint32(name .. ".ack", "Ack", base.DEC) +local f_data = ProtoField.bytes(name .. ".data", "Payload", FT_BYTES) + +p_im.fields = { f_pack_size,f_header_len,f_version,f_option,f_seq,f_ack,f_data } + +local option_name = { + [0] = 'Undefined', + [1] = 'Auth', + [2] = 'AuthReply', + [3] = 'Heartbeat', + [4] = 'HeartbeatReply', + [5] = 'Disconnect', + [6] = 'DisconnectReply', + [7] = 'SendMsg', + [8] = 'SendMsgReply', + [9] = 'ReceiveMsg', + [10] = 'ReceiveMsgReply', + [14] = 'SyncMsgReq', + [15] = 'SyncMsgReply' + } + +local dtalk_proto = Dissector.get("dtalk") +local data_dis = Dissector.get("data") + +function p_im.dissector(buf, pkt, tree) + local offset = 0 + local subtree = tree:add(p_im, buf()) + pkt.cols.protocol = p_im.name + + --packate size + local pkglen_tvbr = buf(offset, 4) + local pkglen = pkglen_tvbr:uint() + subtree:add(f_pack_size, pkglen_tvbr) + offset = offset + 4 + + if pkglen ~= buf:len() then + data_dis:call(buf(2):tvb(), pkt, tree) + else + --deal im parse + -- header Length + subtree:add(f_header_len, buf(offset, 2)) + offset = offset + 2 + --version + subtree:add(f_version, buf(offset, 2)) + offset = offset + 2 + --option + local opt_tvbr = buf(offset, 4) + local optcode = opt_tvbr:uint() + local opttree = subtree:add(f_option, opt_tvbr) + offset = offset + 4 + opttree:append_text(' (' .. option_name[optcode] .. ') ') + pkt.private["dtalk_opt_type"] = optcode + --sequence + subtree:add(f_seq, buf(offset, 4)) + offset = offset + 4 + --acknowledge + subtree:add(f_ack, buf(offset, 4)) + offset = offset + 4 + --data + if buf:len()-offset > 0 then + subtree:add(f_data, buf(offset, buf:len()-offset)) + --sub protocal + if dtalk_proto ~= nil then + dtalk_proto:call(buf(offset):tvb(), pkt, tree) + end + else + subtree:add('Payload:', 'empty') + end + end +end + +local websocket_table = DissectorTable.get("ws.port") + +websocket_table:add(3102, p_im) \ No newline at end of file diff --git a/target/comet.toml b/target/comet.toml new file mode 100644 index 0000000..0c7de68 --- /dev/null +++ b/target/comet.toml @@ -0,0 +1,70 @@ +env="debug" + +[log] + Level="debug" + Mode="console" + Path="" + Display="json" + +[Trace] + ServiceName="" + Gen128Bit=true +[Trace.Sampler] + Type="const" + Param=1.0 +[Trace.Reporter] + LogSpans=true + LocalAgentHostPort="172.16.101.130:6831" + +[reg] + schema = "im" + srvName = "comet" + regAddrs = "127.0.0.1:2379" + +[logicRPCClient] + schema = "im" + srvName = "logic" + dial = "1s" + timeout = "1s" + +[RPCServer] + Network = "tcp" + Addr = ":3109" + Timeout = "1s" + KeepAliveMaxConnectionIdle = "60s" + KeepAliveMaxConnectionAge = "2h" + KeepAliveMaxMaxConnectionAgeGrace = "20s" + KeepAliveTime = "60s" + KeepAliveTimeout = "20s" + +[tcp] + bind = [":3101"] + sndbuf = 4096 + rcvbuf = 4096 + keepalive = false + reader = 32 + readBuf = 1024 + readBufSize = 8192 + writer = 32 + writeBuf = 1024 + writeBufSize = 8192 + +[websocket] + bind = [":3102"] + tlsOpen = false + tlsBind = [":3103"] + certFile = "../../cert.pem" + privateFile = "../../private.pem" + +[protocol] + timer = 32 + timerSize = 2048 + svrProto = 10 + cliProto = 5 + handshakeTimeout = "8s" + minHeartbeat = "5s" + maxHeartbeat = "10s" + +[bucket] + size = 32 + channel = 1024 \ No newline at end of file diff --git a/target/logic.toml b/target/logic.toml new file mode 100644 index 0000000..8f974f1 --- /dev/null +++ b/target/logic.toml @@ -0,0 +1,69 @@ +env="debug" + +[log] + Level="debug" + Mode="console" + Path="" + Display="json" + +[Trace] + ServiceName="" + Gen128Bit=true +[Trace.Sampler] + Type="const" + Param=1.0 +[Trace.Reporter] + LogSpans=true + LocalAgentHostPort="172.16.101.130:6831" + +[reg] + schema = "im" + srvName = "logic" + regAddrs = "127.0.0.1:2379" + +[node] + heartbeat = "4m" + heartbeatMax = 2 + +[backoff] + maxDelay = 300 + baseDelay = 3 + factor = 1.8 + jitter = 0.3 + +[RPCServer] + Network = "tcp" + Addr = ":3119" + Timeout = "1s" + KeepAliveMaxConnectionIdle = "60s" + KeepAliveMaxConnectionAge = "2h" + KeepAliveMaxMaxConnectionAgeGrace = "20s" + KeepAliveTime = "60s" + KeepAliveTimeout = "20s" + + +[CometRPCClient] + schema = "im" + srvName = "comet" + dial = "1s" + timeout = "1s" + +[kafka] + topic = "goim-push-topic" + brokers = ["127.0.0.1:9092"] + +[redis] + network = "tcp" + addr = "127.0.0.1:6379" + active = 60000 + idle = 1024 + dialTimeout = "200ms" + readTimeout = "500ms" + writeTimeout = "500ms" + idleTimeout = "120s" + expire = "30m" + +[[apps]] + appId = "dtalk" + authUrl = "http://127.0.0.1:18002/user/login" + timeout = "1s" \ No newline at end of file