init
This commit is contained in:
54
Makefile
Normal file
54
Makefile
Normal file
@@ -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
|
||||
66
README.md
Normal file
66
README.md
Normal file
@@ -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
|
||||

|
||||
|
||||
## 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)
|
||||
1294
api/comet/grpc/comet.pb.go
Normal file
1294
api/comet/grpc/comet.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
109
api/comet/grpc/comet.proto
Normal file
109
api/comet/grpc/comet.proto
Normal file
@@ -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<string,int32> 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);
|
||||
}
|
||||
281
api/comet/grpc/comet_grpc.pb.go
Normal file
281
api/comet/grpc/comet_grpc.pb.go
Normal file
@@ -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",
|
||||
}
|
||||
6
api/comet/grpc/create.sh
Normal file
6
api/comet/grpc/create.sh
Normal file
@@ -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
|
||||
304
api/comet/grpc/protocol.go
Normal file
304
api/comet/grpc/protocol.go
Normal file
@@ -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
|
||||
}
|
||||
6
api/logic/grpc/create.sh
Normal file
6
api/logic/grpc/create.sh
Normal file
@@ -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
|
||||
1312
api/logic/grpc/logic.pb.go
Normal file
1312
api/logic/grpc/logic.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
117
api/logic/grpc/logic.proto
Normal file
117
api/logic/grpc/logic.proto
Normal file
@@ -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
|
||||
}
|
||||
497
api/logic/grpc/logic_grpc.pb.go
Normal file
497
api/logic/grpc/logic_grpc.pb.go
Normal file
@@ -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",
|
||||
}
|
||||
234
benchmarks/client_tcp/main.go
Normal file
234
benchmarks/client_tcp/main.go
Normal file
@@ -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
|
||||
}
|
||||
253
benchmarks/client_ws/main.go
Normal file
253
benchmarks/client_ws/main.go
Normal file
@@ -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
|
||||
}
|
||||
42
benchmarks/kafka-docker-compose.yml
Normal file
42
benchmarks/kafka-docker-compose.yml
Normal file
@@ -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"
|
||||
29
comet/CHANGELOG.md
Normal file
29
comet/CHANGELOG.md
Normal file
@@ -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**
|
||||
159
comet/bucket.go
Normal file
159
comet/bucket.go
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
102
comet/channel.go
Normal file
102
comet/channel.go
Normal file
@@ -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]
|
||||
}
|
||||
135
comet/cmd/main.go
Normal file
135
comet/cmd/main.go
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
126
comet/comet.go
Normal file
126
comet/comet.go
Normal file
@@ -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
|
||||
}
|
||||
70
comet/conf/comet.toml
Normal file
70
comet/conf/comet.toml
Normal file
@@ -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
|
||||
209
comet/conf/conf.go
Normal file
209
comet/conf/conf.go
Normal file
@@ -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
|
||||
}
|
||||
34
comet/errors/errors.go
Normal file
34
comet/errors/errors.go
Normal file
@@ -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")
|
||||
)
|
||||
119
comet/group.go
Normal file
119
comet/group.go
Normal file
@@ -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
|
||||
}
|
||||
121
comet/group_test.go
Normal file
121
comet/group_test.go
Normal file
@@ -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"))
|
||||
}
|
||||
152
comet/grpc/server.go
Normal file
152
comet/grpc/server.go
Normal file
@@ -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
|
||||
}
|
||||
36
comet/http/result.go
Normal file
36
comet/http/result.go
Normal file
@@ -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,
|
||||
})
|
||||
}
|
||||
46
comet/http/server.go
Normal file
46
comet/http/server.go
Normal file
@@ -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
|
||||
}
|
||||
75
comet/http/statics.go
Normal file
75
comet/http/statics.go
Normal file
@@ -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
|
||||
}
|
||||
7
comet/nodes.go
Normal file
7
comet/nodes.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package comet
|
||||
|
||||
type Node struct {
|
||||
Current *Channel
|
||||
Next *Node
|
||||
Prev *Node
|
||||
}
|
||||
44
comet/operation.go
Normal file
44
comet/operation.go
Normal file
@@ -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
|
||||
}
|
||||
80
comet/ring.go
Normal file
80
comet/ring.go
Normal file
@@ -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{}
|
||||
}
|
||||
78
comet/round.go
Normal file
78
comet/round.go
Normal file
@@ -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])
|
||||
}
|
||||
304
comet/tcp.go
Normal file
304
comet/tcp.go
Normal file
@@ -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
|
||||
}
|
||||
341
comet/ws.go
Normal file
341
comet/ws.go
Normal file
@@ -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
|
||||
}
|
||||
66
common/grpc.go
Normal file
66
common/grpc.go
Normal file
@@ -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...)
|
||||
}
|
||||
BIN
docs/goim-arch.png
Normal file
BIN
docs/goim-arch.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
63
docs/proto.md
Normal file
63
docs/proto.md
Normal file
@@ -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`
|
||||
327
dtask/clock.go
Normal file
327
dtask/clock.go
Normal file
@@ -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()
|
||||
}
|
||||
BIN
dtask/cmd/cli_task
Normal file
BIN
dtask/cmd/cli_task
Normal file
Binary file not shown.
BIN
dtask/cmd/client/dtaskCli
Normal file
BIN
dtask/cmd/client/dtaskCli
Normal file
Binary file not shown.
143
dtask/cmd/client/main.go
Normal file
143
dtask/cmd/client/main.go
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
BIN
dtask/cmd/dtaskSrv
Normal file
BIN
dtask/cmd/dtaskSrv
Normal file
Binary file not shown.
188
dtask/cmd/main.go
Normal file
188
dtask/cmd/main.go
Normal file
@@ -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()
|
||||
}
|
||||
BIN
dtask/cmd/trace.out
Normal file
BIN
dtask/cmd/trace.out
Normal file
Binary file not shown.
6
dtask/debug.go
Normal file
6
dtask/debug.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package dtask
|
||||
|
||||
const (
|
||||
// Debug debug switch
|
||||
Debug = false
|
||||
)
|
||||
99
dtask/job.go
Normal file
99
dtask/job.go
Normal file
@@ -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()
|
||||
}
|
||||
27
dtask/pkg/rbtree/LICENSE
Normal file
27
dtask/pkg/rbtree/LICENSE
Normal file
@@ -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.
|
||||
143
dtask/pkg/rbtree/README.md
Normal file
143
dtask/pkg/rbtree/README.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# Rbtree [](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
|
||||
}
|
||||
42
dtask/pkg/rbtree/example/example_int/example_int.go
Normal file
42
dtask/pkg/rbtree/example/example_int/example_int.go
Normal file
@@ -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
|
||||
}
|
||||
29
dtask/pkg/rbtree/example/example_string/example_string.go
Normal file
29
dtask/pkg/rbtree/example/example_string/example_string.go
Normal file
@@ -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
|
||||
}
|
||||
71
dtask/pkg/rbtree/example/example_struct/example_struct.go
Normal file
71
dtask/pkg/rbtree/example/example_struct/example_struct.go
Normal file
@@ -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
|
||||
}
|
||||
93
dtask/pkg/rbtree/iterator.go
Normal file
93
dtask/pkg/rbtree/iterator.go
Normal file
@@ -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)
|
||||
}
|
||||
426
dtask/pkg/rbtree/rbtree.go
Normal file
426
dtask/pkg/rbtree/rbtree.go
Normal file
@@ -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
|
||||
}
|
||||
200
dtask/pkg/rbtree/rbtree_test.go
Normal file
200
dtask/pkg/rbtree/rbtree_test.go
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
88
dtask/pkg/rbtree/stats.go
Normal file
88
dtask/pkg/rbtree/stats.go
Normal file
@@ -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
|
||||
}
|
||||
152
dtask/pkg/rbtree/stats_test.go
Normal file
152
dtask/pkg/rbtree/stats_test.go
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
21
dtask/pkg/rbtree/util.go
Normal file
21
dtask/pkg/rbtree/util.go
Normal file
@@ -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)
|
||||
}
|
||||
12
dtask/proto/proto.go
Normal file
12
dtask/proto/proto.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package proto
|
||||
|
||||
const (
|
||||
Start = 1
|
||||
Stop = 2
|
||||
)
|
||||
|
||||
type Proto struct {
|
||||
Uid string
|
||||
Opt int
|
||||
Seq int64
|
||||
}
|
||||
33
dtask/safetimer.go
Normal file
33
dtask/safetimer.go
Normal file
@@ -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),
|
||||
}
|
||||
}
|
||||
71
dtask/script/pprof.sh
Normal file
71
dtask/script/pprof.sh
Normal file
@@ -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
|
||||
1308
dtask/script/torch.svg
Normal file
1308
dtask/script/torch.svg
Normal file
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 54 KiB |
40
dtask/task.go
Normal file
40
dtask/task.go
Normal file
@@ -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)
|
||||
}
|
||||
29
go.mod
Normal file
29
go.mod
Normal file
@@ -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
|
||||
876
go.sum
Normal file
876
go.sum
Normal file
@@ -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=
|
||||
38
logic/CHANGELOG.md
Normal file
38
logic/CHANGELOG.md
Normal file
@@ -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**
|
||||
23
logic/auth/auth.go
Normal file
23
logic/auth/auth.go
Normal file
@@ -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)
|
||||
}
|
||||
67
logic/auth/dtalk/auth.go
Normal file
67
logic/auth/dtalk/auth.go
Normal file
@@ -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
|
||||
}
|
||||
17
logic/auth/dtalk/plugin.go
Normal file
17
logic/auth/dtalk/plugin.go
Normal file
@@ -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}
|
||||
}
|
||||
67
logic/auth/zhaobi/auth.go
Normal file
67
logic/auth/zhaobi/auth.go
Normal file
@@ -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
|
||||
}
|
||||
17
logic/auth/zhaobi/plugin.go
Normal file
17
logic/auth/zhaobi/plugin.go
Normal file
@@ -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}
|
||||
}
|
||||
130
logic/cmd/main.go
Normal file
130
logic/cmd/main.go
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
6
logic/cmd/run.sh
Normal file
6
logic/cmd/run.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
# -log_dir=log 日志输出文件夹
|
||||
# -logtostderr=true 打印到标准错误而不是文件。
|
||||
# -alsologtostderr=true 同时打印到标准错误。
|
||||
# -v=4 设置日志级别
|
||||
# -vmodule=main=5 设置单个文件日志级别
|
||||
./main -v=4 -log_dir=log -alsologtostderr=true
|
||||
164
logic/conf/conf.go
Normal file
164
logic/conf/conf.go
Normal file
@@ -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
|
||||
}
|
||||
69
logic/conf/logic.toml
Normal file
69
logic/conf/logic.toml
Normal file
@@ -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"
|
||||
72
logic/dao/dao.go
Normal file
72
logic/dao/dao.go
Normal file
@@ -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)
|
||||
}
|
||||
44
logic/dao/kafka.go
Normal file
44
logic/dao/kafka.go
Normal file
@@ -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
|
||||
}
|
||||
32
logic/dao/main_test.go
Normal file
32
logic/dao/main_test.go
Normal file
@@ -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())
|
||||
}
|
||||
300
logic/dao/redis.go
Normal file
300
logic/dao/redis.go
Normal file
@@ -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
|
||||
}
|
||||
231
logic/dao/redis_test.go
Normal file
231
logic/dao/redis_test.go
Normal file
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
187
logic/grpc/server.go
Normal file
187
logic/grpc/server.go
Normal file
@@ -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
|
||||
}
|
||||
426
logic/logic.go
Normal file
426
logic/logic.go
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
88
naming/balancer/key/key.go
Normal file
88
naming/balancer/key/key.go
Normal file
@@ -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
|
||||
}
|
||||
86
naming/register.go
Normal file
86
naming/register.go
Normal file
@@ -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
|
||||
}
|
||||
112
naming/resolver.go
Normal file
112
naming/resolver.go
Normal file
@@ -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
|
||||
}
|
||||
43
script/build/quick_build.sh
Normal file
43
script/build/quick_build.sh
Normal file
@@ -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"
|
||||
|
||||
|
||||
26
script/build/util.sh
Normal file
26
script/build/util.sh
Normal file
@@ -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}"
|
||||
}
|
||||
BIN
script/gen-proto/protoc-gen-go-grpc_v1.1.0
Normal file
BIN
script/gen-proto/protoc-gen-go-grpc_v1.1.0
Normal file
Binary file not shown.
BIN
script/gen-proto/protoc-gen-go_v1.27.1
Normal file
BIN
script/gen-proto/protoc-gen-go_v1.27.1
Normal file
Binary file not shown.
BIN
script/gen-proto/protoc_v3.19.1
Normal file
BIN
script/gen-proto/protoc_v3.19.1
Normal file
Binary file not shown.
82
script/im.lua
Normal file
82
script/im.lua
Normal file
@@ -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)
|
||||
70
target/comet.toml
Normal file
70
target/comet.toml
Normal file
@@ -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
|
||||
69
target/logic.toml
Normal file
69
target/logic.toml
Normal file
@@ -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"
|
||||
Reference in New Issue
Block a user