| @@ -6,6 +6,7 @@ import ( | |||||
| log "gitlink.org.cn/cloudream/common/pkgs/logger" | log "gitlink.org.cn/cloudream/common/pkgs/logger" | ||||
| c "gitlink.org.cn/cloudream/common/utils/config" | c "gitlink.org.cn/cloudream/common/utils/config" | ||||
| stgmodels "gitlink.org.cn/cloudream/storage/common/models" | stgmodels "gitlink.org.cn/cloudream/storage/common/models" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/connectivity" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/grpc" | "gitlink.org.cn/cloudream/storage/common/pkgs/grpc" | ||||
| stgmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq" | stgmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq" | ||||
| ) | ) | ||||
| @@ -19,6 +20,7 @@ type Config struct { | |||||
| RabbitMQ stgmq.Config `json:"rabbitMQ"` | RabbitMQ stgmq.Config `json:"rabbitMQ"` | ||||
| IPFS ipfs.Config `json:"ipfs"` | IPFS ipfs.Config `json:"ipfs"` | ||||
| DistLock distlock.Config `json:"distlock"` | DistLock distlock.Config `json:"distlock"` | ||||
| Connectivity connectivity.Config `json:"connectivity"` | |||||
| } | } | ||||
| var cfg Config | var cfg Config | ||||
| @@ -0,0 +1,11 @@ | |||||
| package grpc | |||||
| import ( | |||||
| "context" | |||||
| agtrpc "gitlink.org.cn/cloudream/storage/common/pkgs/grpc/agent" | |||||
| ) | |||||
| func (s *Service) Ping(context.Context, *agtrpc.PingReq) (*agtrpc.PingResp, error) { | |||||
| return &agtrpc.PingResp{}, nil | |||||
| } | |||||
| @@ -64,7 +64,8 @@ func (t *CreatePackage) Execute(task *task.Task[TaskContext], ctx TaskContext, c | |||||
| } | } | ||||
| uploadRet, err := cmd.NewUploadObjects(t.userID, createResp.PackageID, t.objIter, t.nodeAffinity).Execute(&cmd.UploadObjectsContext{ | uploadRet, err := cmd.NewUploadObjects(t.userID, createResp.PackageID, t.objIter, t.nodeAffinity).Execute(&cmd.UploadObjectsContext{ | ||||
| Distlock: ctx.distlock, | |||||
| Distlock: ctx.distlock, | |||||
| Connectivity: ctx.connectivity, | |||||
| }) | }) | ||||
| if err != nil { | if err != nil { | ||||
| err = fmt.Errorf("uploading objects: %w", err) | err = fmt.Errorf("uploading objects: %w", err) | ||||
| @@ -3,12 +3,14 @@ package task | |||||
| import ( | import ( | ||||
| "gitlink.org.cn/cloudream/common/pkgs/distlock" | "gitlink.org.cn/cloudream/common/pkgs/distlock" | ||||
| "gitlink.org.cn/cloudream/common/pkgs/task" | "gitlink.org.cn/cloudream/common/pkgs/task" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/connectivity" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch" | "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch" | ||||
| ) | ) | ||||
| type TaskContext struct { | type TaskContext struct { | ||||
| distlock *distlock.Service | |||||
| sw *ioswitch.Switch | |||||
| distlock *distlock.Service | |||||
| sw *ioswitch.Switch | |||||
| connectivity *connectivity.Collector | |||||
| } | } | ||||
| // 需要在Task结束后主动调用,completing函数将在Manager加锁期间被调用, | // 需要在Task结束后主动调用,completing函数将在Manager加锁期间被调用, | ||||
| @@ -23,9 +25,10 @@ type Task = task.Task[TaskContext] | |||||
| type CompleteOption = task.CompleteOption | type CompleteOption = task.CompleteOption | ||||
| func NewManager(distlock *distlock.Service, sw *ioswitch.Switch) Manager { | |||||
| func NewManager(distlock *distlock.Service, sw *ioswitch.Switch, connectivity *connectivity.Collector) Manager { | |||||
| return task.NewManager(TaskContext{ | return task.NewManager(TaskContext{ | ||||
| distlock: distlock, | |||||
| sw: sw, | |||||
| distlock: distlock, | |||||
| sw: sw, | |||||
| connectivity: connectivity, | |||||
| }) | }) | ||||
| } | } | ||||
| @@ -7,9 +7,11 @@ import ( | |||||
| "sync" | "sync" | ||||
| log "gitlink.org.cn/cloudream/common/pkgs/logger" | log "gitlink.org.cn/cloudream/common/pkgs/logger" | ||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | |||||
| "gitlink.org.cn/cloudream/storage/agent/internal/config" | "gitlink.org.cn/cloudream/storage/agent/internal/config" | ||||
| "gitlink.org.cn/cloudream/storage/agent/internal/task" | "gitlink.org.cn/cloudream/storage/agent/internal/task" | ||||
| stgglb "gitlink.org.cn/cloudream/storage/common/globals" | stgglb "gitlink.org.cn/cloudream/storage/common/globals" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/connectivity" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/distlock" | "gitlink.org.cn/cloudream/storage/common/pkgs/distlock" | ||||
| agtrpc "gitlink.org.cn/cloudream/storage/common/pkgs/grpc/agent" | agtrpc "gitlink.org.cn/cloudream/storage/common/pkgs/grpc/agent" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch" | "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch" | ||||
| @@ -20,6 +22,7 @@ import ( | |||||
| "google.golang.org/grpc" | "google.golang.org/grpc" | ||||
| agtmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/agent" | agtmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/agent" | ||||
| coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator" | |||||
| grpcsvc "gitlink.org.cn/cloudream/storage/agent/internal/grpc" | grpcsvc "gitlink.org.cn/cloudream/storage/agent/internal/grpc" | ||||
| cmdsvc "gitlink.org.cn/cloudream/storage/agent/internal/mq" | cmdsvc "gitlink.org.cn/cloudream/storage/agent/internal/mq" | ||||
| @@ -49,6 +52,41 @@ func main() { | |||||
| stgglb.InitAgentRPCPool(&agtrpc.PoolConfig{}) | stgglb.InitAgentRPCPool(&agtrpc.PoolConfig{}) | ||||
| stgglb.InitIPFSPool(&config.Cfg().IPFS) | stgglb.InitIPFSPool(&config.Cfg().IPFS) | ||||
| // 启动网络连通性检测,并就地检测一次 | |||||
| conCol := connectivity.NewCollector(&config.Cfg().Connectivity, func(collector *connectivity.Collector) { | |||||
| log := log.WithField("Connectivity", "") | |||||
| coorCli, err := stgglb.CoordinatorMQPool.Acquire() | |||||
| if err != nil { | |||||
| log.Warnf("acquire coordinator mq failed, err: %s", err.Error()) | |||||
| return | |||||
| } | |||||
| defer stgglb.CoordinatorMQPool.Release(coorCli) | |||||
| cons := collector.GetAll() | |||||
| nodeCons := make([]cdssdk.NodeConnectivity, 0, len(cons)) | |||||
| for _, con := range cons { | |||||
| var delay *float32 | |||||
| if con.Delay != nil { | |||||
| v := float32(con.Delay.Microseconds()) / 1000 | |||||
| delay = &v | |||||
| } | |||||
| nodeCons = append(nodeCons, cdssdk.NodeConnectivity{ | |||||
| FromNodeID: *stgglb.Local.NodeID, | |||||
| ToNodeID: con.ToNodeID, | |||||
| Delay: delay, | |||||
| TestTime: con.TestTime, | |||||
| }) | |||||
| } | |||||
| _, err = coorCli.UpdateNodeConnectivities(coormq.ReqUpdateNodeConnectivities(nodeCons)) | |||||
| if err != nil { | |||||
| log.Warnf("update node connectivities: %v", err) | |||||
| } | |||||
| }) | |||||
| conCol.CollectInPlace() | |||||
| distlock, err := distlock.NewService(&config.Cfg().DistLock) | distlock, err := distlock.NewService(&config.Cfg().DistLock) | ||||
| if err != nil { | if err != nil { | ||||
| log.Fatalf("new ipfs failed, err: %s", err.Error()) | log.Fatalf("new ipfs failed, err: %s", err.Error()) | ||||
| @@ -60,7 +98,7 @@ func main() { | |||||
| wg := sync.WaitGroup{} | wg := sync.WaitGroup{} | ||||
| wg.Add(4) | wg.Add(4) | ||||
| taskMgr := task.NewManager(distlock, &sw) | |||||
| taskMgr := task.NewManager(distlock, &sw, &conCol) | |||||
| // 启动命令服务器 | // 启动命令服务器 | ||||
| // TODO 需要设计AgentID持久化机制 | // TODO 需要设计AgentID持久化机制 | ||||
| @@ -74,8 +112,6 @@ func main() { | |||||
| go serveAgentServer(agtSvr, &wg) | go serveAgentServer(agtSvr, &wg) | ||||
| // go reportStatus(&wg) //网络延迟感知 | |||||
| //面向客户端收发数据 | //面向客户端收发数据 | ||||
| listenAddr := config.Cfg().GRPC.MakeListenAddress() | listenAddr := config.Cfg().GRPC.MakeListenAddress() | ||||
| lis, err := net.Listen("tcp", listenAddr) | lis, err := net.Listen("tcp", listenAddr) | ||||
| @@ -6,17 +6,19 @@ import ( | |||||
| "gitlink.org.cn/cloudream/common/pkgs/logger" | "gitlink.org.cn/cloudream/common/pkgs/logger" | ||||
| "gitlink.org.cn/cloudream/common/utils/config" | "gitlink.org.cn/cloudream/common/utils/config" | ||||
| stgmodels "gitlink.org.cn/cloudream/storage/common/models" | stgmodels "gitlink.org.cn/cloudream/storage/common/models" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/connectivity" | |||||
| agtrpc "gitlink.org.cn/cloudream/storage/common/pkgs/grpc/agent" | agtrpc "gitlink.org.cn/cloudream/storage/common/pkgs/grpc/agent" | ||||
| stgmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq" | stgmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq" | ||||
| ) | ) | ||||
| type Config struct { | type Config struct { | ||||
| Local stgmodels.LocalMachineInfo `json:"local"` | |||||
| AgentGRPC agtrpc.PoolConfig `json:"agentGRPC"` | |||||
| Logger logger.Config `json:"logger"` | |||||
| RabbitMQ stgmq.Config `json:"rabbitMQ"` | |||||
| IPFS *ipfs.Config `json:"ipfs"` // 此字段非空代表客户端上存在ipfs daemon | |||||
| DistLock distlock.Config `json:"distlock"` | |||||
| Local stgmodels.LocalMachineInfo `json:"local"` | |||||
| AgentGRPC agtrpc.PoolConfig `json:"agentGRPC"` | |||||
| Logger logger.Config `json:"logger"` | |||||
| RabbitMQ stgmq.Config `json:"rabbitMQ"` | |||||
| IPFS *ipfs.Config `json:"ipfs"` // 此字段非空代表客户端上存在ipfs daemon | |||||
| DistLock distlock.Config `json:"distlock"` | |||||
| Connectivity connectivity.Config `json:"connectivity"` | |||||
| } | } | ||||
| var cfg Config | var cfg Config | ||||
| @@ -3,10 +3,12 @@ package task | |||||
| import ( | import ( | ||||
| "gitlink.org.cn/cloudream/common/pkgs/distlock" | "gitlink.org.cn/cloudream/common/pkgs/distlock" | ||||
| "gitlink.org.cn/cloudream/common/pkgs/task" | "gitlink.org.cn/cloudream/common/pkgs/task" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/connectivity" | |||||
| ) | ) | ||||
| type TaskContext struct { | type TaskContext struct { | ||||
| distlock *distlock.Service | |||||
| distlock *distlock.Service | |||||
| connectivity *connectivity.Collector | |||||
| } | } | ||||
| // 需要在Task结束后主动调用,completing函数将在Manager加锁期间被调用, | // 需要在Task结束后主动调用,completing函数将在Manager加锁期间被调用, | ||||
| @@ -21,8 +23,9 @@ type Task = task.Task[TaskContext] | |||||
| type CompleteOption = task.CompleteOption | type CompleteOption = task.CompleteOption | ||||
| func NewManager(distlock *distlock.Service) Manager { | |||||
| func NewManager(distlock *distlock.Service, connectivity *connectivity.Collector) Manager { | |||||
| return task.NewManager(TaskContext{ | return task.NewManager(TaskContext{ | ||||
| distlock: distlock, | |||||
| distlock: distlock, | |||||
| connectivity: connectivity, | |||||
| }) | }) | ||||
| } | } | ||||
| @@ -25,7 +25,8 @@ func NewUploadObjects(userID cdssdk.UserID, packageID cdssdk.PackageID, objectIt | |||||
| func (t *UploadObjects) Execute(task *task.Task[TaskContext], ctx TaskContext, complete CompleteFn) { | func (t *UploadObjects) Execute(task *task.Task[TaskContext], ctx TaskContext, complete CompleteFn) { | ||||
| ret, err := t.cmd.Execute(&cmd.UploadObjectsContext{ | ret, err := t.cmd.Execute(&cmd.UploadObjectsContext{ | ||||
| Distlock: ctx.distlock, | |||||
| Distlock: ctx.distlock, | |||||
| Connectivity: ctx.connectivity, | |||||
| }) | }) | ||||
| t.Result = ret | t.Result = ret | ||||
| @@ -12,6 +12,7 @@ import ( | |||||
| "gitlink.org.cn/cloudream/storage/client/internal/services" | "gitlink.org.cn/cloudream/storage/client/internal/services" | ||||
| "gitlink.org.cn/cloudream/storage/client/internal/task" | "gitlink.org.cn/cloudream/storage/client/internal/task" | ||||
| stgglb "gitlink.org.cn/cloudream/storage/common/globals" | stgglb "gitlink.org.cn/cloudream/storage/common/globals" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/connectivity" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/distlock" | "gitlink.org.cn/cloudream/storage/common/pkgs/distlock" | ||||
| ) | ) | ||||
| @@ -37,6 +38,10 @@ func main() { | |||||
| stgglb.InitIPFSPool(config.Cfg().IPFS) | stgglb.InitIPFSPool(config.Cfg().IPFS) | ||||
| } | } | ||||
| // 启动网络连通性检测,并就地检测一次 | |||||
| conCol := connectivity.NewCollector(&config.Cfg().Connectivity, nil) | |||||
| conCol.CollectInPlace() | |||||
| distlockSvc, err := distlock.NewService(&config.Cfg().DistLock) | distlockSvc, err := distlock.NewService(&config.Cfg().DistLock) | ||||
| if err != nil { | if err != nil { | ||||
| logger.Warnf("new distlock service failed, err: %s", err.Error()) | logger.Warnf("new distlock service failed, err: %s", err.Error()) | ||||
| @@ -44,7 +49,7 @@ func main() { | |||||
| } | } | ||||
| go serveDistLock(distlockSvc) | go serveDistLock(distlockSvc) | ||||
| taskMgr := task.NewManager(distlockSvc) | |||||
| taskMgr := task.NewManager(distlockSvc, &conCol) | |||||
| svc, err := services.NewService(distlockSvc, &taskMgr) | svc, err := services.NewService(distlockSvc, &taskMgr) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -32,5 +32,8 @@ | |||||
| "etcdLockLeaseTimeSec": 5, | "etcdLockLeaseTimeSec": 5, | ||||
| "randomReleasingDelayMs": 3000, | "randomReleasingDelayMs": 3000, | ||||
| "serviceDescription": "I am a agent" | "serviceDescription": "I am a agent" | ||||
| }, | |||||
| "connectivity": { | |||||
| "testInterval": 300 | |||||
| } | } | ||||
| } | } | ||||
| @@ -25,5 +25,8 @@ | |||||
| "etcdLockLeaseTimeSec": 5, | "etcdLockLeaseTimeSec": 5, | ||||
| "randomReleasingDelayMs": 3000, | "randomReleasingDelayMs": 3000, | ||||
| "serviceDescription": "I am a client" | "serviceDescription": "I am a client" | ||||
| }, | |||||
| "connectivity": { | |||||
| "testInterval": 300 | |||||
| } | } | ||||
| } | } | ||||
| @@ -52,12 +52,13 @@ insert into | |||||
| values | values | ||||
| (1, "HuaWei-Cloud", 1, "/", "Online"); | (1, "HuaWei-Cloud", 1, "/", "Online"); | ||||
| create table NodeDelay ( | |||||
| SourceNodeID int not null comment '发起检测的节点ID', | |||||
| DestinationNodeID int not null comment '被检测节点的ID', | |||||
| DelayInMs int not null comment '发起节点与被检测节点间延迟(毫秒)', | |||||
| primary key(SourceNodeID, DestinationNodeID) | |||||
| ) comment = '节点延迟表'; | |||||
| create table NodeConnectivity ( | |||||
| FromNodeID int not null comment '发起检测的节点ID', | |||||
| ToNodeID int not null comment '被检测节点的ID', | |||||
| Delay float comment '发起节点与被检测节点间延迟(毫秒),为null代表节点不可达', | |||||
| TestTime timestamp comment '进行连通性测试的时间', | |||||
| primary key(FromNodeID, ToNodeID) | |||||
| ) comment = '节点连通性表'; | |||||
| create table User ( | create table User ( | ||||
| UserID int not null primary key comment '用户ID', | UserID int not null primary key comment '用户ID', | ||||
| @@ -3,6 +3,7 @@ package cmd | |||||
| import ( | import ( | ||||
| "fmt" | "fmt" | ||||
| "io" | "io" | ||||
| "math" | |||||
| "math/rand" | "math/rand" | ||||
| "time" | "time" | ||||
| @@ -11,8 +12,10 @@ import ( | |||||
| "gitlink.org.cn/cloudream/common/pkgs/distlock" | "gitlink.org.cn/cloudream/common/pkgs/distlock" | ||||
| "gitlink.org.cn/cloudream/common/pkgs/logger" | "gitlink.org.cn/cloudream/common/pkgs/logger" | ||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | ||||
| "gitlink.org.cn/cloudream/common/utils/sort2" | |||||
| stgglb "gitlink.org.cn/cloudream/storage/common/globals" | stgglb "gitlink.org.cn/cloudream/storage/common/globals" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/connectivity" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/distlock/reqbuilder" | "gitlink.org.cn/cloudream/storage/common/pkgs/distlock/reqbuilder" | ||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/iterator" | "gitlink.org.cn/cloudream/storage/common/pkgs/iterator" | ||||
| agtmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/agent" | agtmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/agent" | ||||
| @@ -39,11 +42,13 @@ type ObjectUploadResult struct { | |||||
| type UploadNodeInfo struct { | type UploadNodeInfo struct { | ||||
| Node cdssdk.Node | Node cdssdk.Node | ||||
| Delay time.Duration | |||||
| IsSameLocation bool | IsSameLocation bool | ||||
| } | } | ||||
| type UploadObjectsContext struct { | type UploadObjectsContext struct { | ||||
| Distlock *distlock.Service | |||||
| Distlock *distlock.Service | |||||
| Connectivity *connectivity.Collector | |||||
| } | } | ||||
| func NewUploadObjects(userID cdssdk.UserID, packageID cdssdk.PackageID, objIter iterator.UploadingObjectIterator, nodeAffinity *cdssdk.NodeID) *UploadObjects { | func NewUploadObjects(userID cdssdk.UserID, packageID cdssdk.PackageID, objIter iterator.UploadingObjectIterator, nodeAffinity *cdssdk.NodeID) *UploadObjects { | ||||
| @@ -68,12 +73,24 @@ func (t *UploadObjects) Execute(ctx *UploadObjectsContext) (*UploadObjectsResult | |||||
| return nil, fmt.Errorf("getting user nodes: %w", err) | return nil, fmt.Errorf("getting user nodes: %w", err) | ||||
| } | } | ||||
| cons := ctx.Connectivity.GetAll() | |||||
| userNodes := lo.Map(getUserNodesResp.Nodes, func(node cdssdk.Node, index int) UploadNodeInfo { | userNodes := lo.Map(getUserNodesResp.Nodes, func(node cdssdk.Node, index int) UploadNodeInfo { | ||||
| delay := time.Duration(math.MaxInt64) | |||||
| con, ok := cons[node.NodeID] | |||||
| if ok && con.Delay != nil { | |||||
| delay = *con.Delay | |||||
| } | |||||
| return UploadNodeInfo{ | return UploadNodeInfo{ | ||||
| Node: node, | Node: node, | ||||
| Delay: delay, | |||||
| IsSameLocation: node.LocationID == stgglb.Local.LocationID, | IsSameLocation: node.LocationID == stgglb.Local.LocationID, | ||||
| } | } | ||||
| }) | }) | ||||
| if len(userNodes) == 0 { | |||||
| return nil, fmt.Errorf("user no available nodes") | |||||
| } | |||||
| // 给上传节点的IPFS加锁 | // 给上传节点的IPFS加锁 | ||||
| ipfsReqBlder := reqbuilder.NewBuilder() | ipfsReqBlder := reqbuilder.NewBuilder() | ||||
| @@ -109,7 +126,7 @@ func (t *UploadObjects) Execute(ctx *UploadObjectsContext) (*UploadObjectsResult | |||||
| // chooseUploadNode 选择一个上传文件的节点 | // chooseUploadNode 选择一个上传文件的节点 | ||||
| // 1. 选择设置了亲和性的节点 | // 1. 选择设置了亲和性的节点 | ||||
| // 2. 从与当前客户端相同地域的节点中随机选一个 | // 2. 从与当前客户端相同地域的节点中随机选一个 | ||||
| // 3. 没有用的话从所有节点中随机选一个 | |||||
| // 3. 没有的话从所有节点选择延迟最低的节点 | |||||
| func chooseUploadNode(nodes []UploadNodeInfo, nodeAffinity *cdssdk.NodeID) UploadNodeInfo { | func chooseUploadNode(nodes []UploadNodeInfo, nodeAffinity *cdssdk.NodeID) UploadNodeInfo { | ||||
| if nodeAffinity != nil { | if nodeAffinity != nil { | ||||
| aff, ok := lo.Find(nodes, func(node UploadNodeInfo) bool { return node.Node.NodeID == *nodeAffinity }) | aff, ok := lo.Find(nodes, func(node UploadNodeInfo) bool { return node.Node.NodeID == *nodeAffinity }) | ||||
| @@ -123,7 +140,10 @@ func chooseUploadNode(nodes []UploadNodeInfo, nodeAffinity *cdssdk.NodeID) Uploa | |||||
| return sameLocationNodes[rand.Intn(len(sameLocationNodes))] | return sameLocationNodes[rand.Intn(len(sameLocationNodes))] | ||||
| } | } | ||||
| return nodes[rand.Intn(len(nodes))] | |||||
| // 选择延迟最低的节点 | |||||
| nodes = sort2.Sort(nodes, func(e1, e2 UploadNodeInfo) int { return sort2.Cmp(e1.Delay, e2.Delay) }) | |||||
| return nodes[0] | |||||
| } | } | ||||
| func uploadAndUpdatePackage(packageID cdssdk.PackageID, objectIter iterator.UploadingObjectIterator, userNodes []UploadNodeInfo, nodeAffinity *cdssdk.NodeID) ([]ObjectUploadResult, error) { | func uploadAndUpdatePackage(packageID cdssdk.PackageID, objectIter iterator.UploadingObjectIterator, userNodes []UploadNodeInfo, nodeAffinity *cdssdk.NodeID) ([]ObjectUploadResult, error) { | ||||
| @@ -0,0 +1,198 @@ | |||||
| package connectivity | |||||
| import ( | |||||
| "math/rand" | |||||
| "sync" | |||||
| "time" | |||||
| "gitlink.org.cn/cloudream/common/pkgs/logger" | |||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | |||||
| stgglb "gitlink.org.cn/cloudream/storage/common/globals" | |||||
| coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator" | |||||
| ) | |||||
| type Connectivity struct { | |||||
| ToNodeID cdssdk.NodeID | |||||
| Delay *time.Duration | |||||
| TestTime time.Time | |||||
| } | |||||
| type Collector struct { | |||||
| cfg *Config | |||||
| onCollected func(collector *Collector) | |||||
| collectNow chan any | |||||
| close chan any | |||||
| connectivities map[cdssdk.NodeID]Connectivity | |||||
| lock *sync.RWMutex | |||||
| } | |||||
| func NewCollector(cfg *Config, onCollected func(collector *Collector)) Collector { | |||||
| rpt := Collector{ | |||||
| cfg: cfg, | |||||
| collectNow: make(chan any), | |||||
| close: make(chan any), | |||||
| connectivities: make(map[cdssdk.NodeID]Connectivity), | |||||
| lock: &sync.RWMutex{}, | |||||
| onCollected: onCollected, | |||||
| } | |||||
| go rpt.serve() | |||||
| return rpt | |||||
| } | |||||
| func (r *Collector) Get(nodeID cdssdk.NodeID) *Connectivity { | |||||
| r.lock.RLock() | |||||
| defer r.lock.RUnlock() | |||||
| con, ok := r.connectivities[nodeID] | |||||
| if ok { | |||||
| return &con | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func (r *Collector) GetAll() map[cdssdk.NodeID]Connectivity { | |||||
| r.lock.RLock() | |||||
| defer r.lock.RUnlock() | |||||
| ret := make(map[cdssdk.NodeID]Connectivity) | |||||
| for k, v := range r.connectivities { | |||||
| ret[k] = v | |||||
| } | |||||
| return ret | |||||
| } | |||||
| // 启动一次收集 | |||||
| func (r *Collector) CollecNow() { | |||||
| select { | |||||
| case r.collectNow <- nil: | |||||
| default: | |||||
| } | |||||
| } | |||||
| // 就地进行收集,会阻塞当前线程 | |||||
| func (r *Collector) CollectInPlace() { | |||||
| r.testing() | |||||
| } | |||||
| func (r *Collector) Close() { | |||||
| select { | |||||
| case r.close <- nil: | |||||
| default: | |||||
| } | |||||
| } | |||||
| func (r *Collector) serve() { | |||||
| log := logger.WithType[Collector]("") | |||||
| log.Info("start connectivity reporter") | |||||
| // 为了防止同时启动的节点会集中进行Ping,所以第一次上报间隔为0-TestInterval秒之间随机 | |||||
| startup := true | |||||
| firstReportDelay := time.Duration(float64(r.cfg.TestInterval) * float64(time.Second) * rand.Float64()) | |||||
| ticker := time.NewTicker(firstReportDelay) | |||||
| loop: | |||||
| for { | |||||
| select { | |||||
| case <-ticker.C: | |||||
| r.testing() | |||||
| if startup { | |||||
| startup = false | |||||
| ticker.Reset(time.Duration(r.cfg.TestInterval) * time.Second) | |||||
| } | |||||
| case <-r.collectNow: | |||||
| r.testing() | |||||
| case <-r.close: | |||||
| ticker.Stop() | |||||
| break loop | |||||
| } | |||||
| } | |||||
| log.Info("stop connectivity reporter") | |||||
| } | |||||
| func (r *Collector) testing() { | |||||
| log := logger.WithType[Collector]("") | |||||
| log.Debug("do testing") | |||||
| coorCli, err := stgglb.CoordinatorMQPool.Acquire() | |||||
| if err != nil { | |||||
| return | |||||
| } | |||||
| defer stgglb.CoordinatorMQPool.Release(coorCli) | |||||
| getNodeResp, err := coorCli.GetNodes(coormq.NewGetNodes(nil)) | |||||
| if err != nil { | |||||
| return | |||||
| } | |||||
| wg := sync.WaitGroup{} | |||||
| cons := make([]Connectivity, len(getNodeResp.Nodes)) | |||||
| for i, node := range getNodeResp.Nodes { | |||||
| tmpIdx := i | |||||
| tmpNode := node | |||||
| wg.Add(1) | |||||
| go func() { | |||||
| defer wg.Done() | |||||
| cons[tmpIdx] = r.ping(tmpNode) | |||||
| }() | |||||
| } | |||||
| wg.Wait() | |||||
| r.lock.Lock() | |||||
| // 删除所有node的记录,然后重建,避免node数量变化时导致残余数据 | |||||
| r.connectivities = make(map[cdssdk.NodeID]Connectivity) | |||||
| for _, con := range cons { | |||||
| r.connectivities[con.ToNodeID] = con | |||||
| } | |||||
| r.lock.Unlock() | |||||
| if r.onCollected != nil { | |||||
| r.onCollected(r) | |||||
| } | |||||
| } | |||||
| func (r *Collector) ping(node cdssdk.Node) Connectivity { | |||||
| log := logger.WithType[Collector]("").WithField("NodeID", node.NodeID) | |||||
| ip := node.ExternalIP | |||||
| port := node.ExternalGRPCPort | |||||
| if node.LocationID == stgglb.Local.LocationID { | |||||
| ip = node.LocalIP | |||||
| port = node.LocalGRPCPort | |||||
| } | |||||
| agtCli, err := stgglb.AgentRPCPool.Acquire(ip, port) | |||||
| if err != nil { | |||||
| log.Warnf("new agent %v:%v rpc client: %w", ip, port, err) | |||||
| return Connectivity{ | |||||
| ToNodeID: node.NodeID, | |||||
| Delay: nil, | |||||
| TestTime: time.Now(), | |||||
| } | |||||
| } | |||||
| defer stgglb.AgentRPCPool.Release(agtCli) | |||||
| start := time.Now() | |||||
| err = agtCli.Ping(*stgglb.Local.NodeID) | |||||
| if err != nil { | |||||
| log.Warnf("ping: %v", err) | |||||
| return Connectivity{ | |||||
| ToNodeID: node.NodeID, | |||||
| Delay: nil, | |||||
| TestTime: time.Now(), | |||||
| } | |||||
| } | |||||
| // 此时间差为一个来回的时间,因此单程延迟需要除以2 | |||||
| delay := time.Since(start) / 2 | |||||
| return Connectivity{ | |||||
| ToNodeID: node.NodeID, | |||||
| Delay: &delay, | |||||
| TestTime: time.Now(), | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,5 @@ | |||||
| package connectivity | |||||
| type Config struct { | |||||
| TestInterval int `json:"testInterval"` // 进行测试的间隔 | |||||
| } | |||||
| @@ -20,12 +20,6 @@ type Storage struct { | |||||
| State string `db:"State" json:"state"` | State string `db:"State" json:"state"` | ||||
| } | } | ||||
| type NodeDelay struct { | |||||
| SourceNodeID int64 `db:"SourceNodeID"` | |||||
| DestinationNodeID int64 `db:"DestinationNodeID"` | |||||
| DelayInMs int `db:"DelayInMs"` | |||||
| } | |||||
| type User struct { | type User struct { | ||||
| UserID cdssdk.UserID `db:"UserID" json:"userID"` | UserID cdssdk.UserID `db:"UserID" json:"userID"` | ||||
| Password string `db:"PassWord" json:"password"` | Password string `db:"PassWord" json:"password"` | ||||
| @@ -52,6 +46,8 @@ type Package = cdssdk.Package | |||||
| type Object = cdssdk.Object | type Object = cdssdk.Object | ||||
| type NodeConnectivity = cdssdk.NodeConnectivity | |||||
| // 由于Object的Redundancy字段是interface,所以不能直接将查询结果scan成Object,必须先scan成TempObject, | // 由于Object的Redundancy字段是interface,所以不能直接将查询结果scan成Object,必须先scan成TempObject, | ||||
| // 再.ToObject()转成Object | // 再.ToObject()转成Object | ||||
| type TempObject struct { | type TempObject struct { | ||||
| @@ -0,0 +1,32 @@ | |||||
| package db | |||||
| import ( | |||||
| "github.com/jmoiron/sqlx" | |||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/db/model" | |||||
| ) | |||||
| type NodeConnectivityDB struct { | |||||
| *DB | |||||
| } | |||||
| func (db *DB) NodeConnectivity() *NodeConnectivityDB { | |||||
| return &NodeConnectivityDB{DB: db} | |||||
| } | |||||
| func (db *NodeConnectivityDB) BatchGetByFromNode(ctx SQLContext, nodeIDs []cdssdk.NodeID) ([]model.NodeConnectivity, error) { | |||||
| var ret []model.NodeConnectivity | |||||
| sql, args, err := sqlx.In("select * from NodeConnectivity where NodeID in (?)", nodeIDs) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| return ret, sqlx.Select(ctx, &ret, sql, args...) | |||||
| } | |||||
| func (db *NodeConnectivityDB) BatchUpdateOrCreate(ctx SQLContext, cons []model.NodeConnectivity) error { | |||||
| return BatchNamedExec(ctx, | |||||
| "insert into NodeConnectivity(FromNodeID, ToNodeID, Delay, TestTime) values(:FromNodeID, :ToNodeID, :Delay, :TestTime) as new"+ | |||||
| " on duplicate key update Delay = new.Delay, TestTime = new.TestTime", 4, cons, nil) | |||||
| } | |||||
| @@ -386,6 +386,91 @@ func (x *FetchStreamReq) GetStreamID() string { | |||||
| return "" | return "" | ||||
| } | } | ||||
| type PingReq struct { | |||||
| state protoimpl.MessageState | |||||
| sizeCache protoimpl.SizeCache | |||||
| unknownFields protoimpl.UnknownFields | |||||
| FromNodeID int64 `protobuf:"varint,1,opt,name=FromNodeID,proto3" json:"FromNodeID,omitempty"` | |||||
| } | |||||
| func (x *PingReq) Reset() { | |||||
| *x = PingReq{} | |||||
| if protoimpl.UnsafeEnabled { | |||||
| mi := &file_pkgs_grpc_agent_agent_proto_msgTypes[6] | |||||
| ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |||||
| ms.StoreMessageInfo(mi) | |||||
| } | |||||
| } | |||||
| func (x *PingReq) String() string { | |||||
| return protoimpl.X.MessageStringOf(x) | |||||
| } | |||||
| func (*PingReq) ProtoMessage() {} | |||||
| func (x *PingReq) ProtoReflect() protoreflect.Message { | |||||
| mi := &file_pkgs_grpc_agent_agent_proto_msgTypes[6] | |||||
| if protoimpl.UnsafeEnabled && x != nil { | |||||
| ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |||||
| if ms.LoadMessageInfo() == nil { | |||||
| ms.StoreMessageInfo(mi) | |||||
| } | |||||
| return ms | |||||
| } | |||||
| return mi.MessageOf(x) | |||||
| } | |||||
| // Deprecated: Use PingReq.ProtoReflect.Descriptor instead. | |||||
| func (*PingReq) Descriptor() ([]byte, []int) { | |||||
| return file_pkgs_grpc_agent_agent_proto_rawDescGZIP(), []int{6} | |||||
| } | |||||
| func (x *PingReq) GetFromNodeID() int64 { | |||||
| if x != nil { | |||||
| return x.FromNodeID | |||||
| } | |||||
| return 0 | |||||
| } | |||||
| type PingResp struct { | |||||
| state protoimpl.MessageState | |||||
| sizeCache protoimpl.SizeCache | |||||
| unknownFields protoimpl.UnknownFields | |||||
| } | |||||
| func (x *PingResp) Reset() { | |||||
| *x = PingResp{} | |||||
| if protoimpl.UnsafeEnabled { | |||||
| mi := &file_pkgs_grpc_agent_agent_proto_msgTypes[7] | |||||
| ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |||||
| ms.StoreMessageInfo(mi) | |||||
| } | |||||
| } | |||||
| func (x *PingResp) String() string { | |||||
| return protoimpl.X.MessageStringOf(x) | |||||
| } | |||||
| func (*PingResp) ProtoMessage() {} | |||||
| func (x *PingResp) ProtoReflect() protoreflect.Message { | |||||
| mi := &file_pkgs_grpc_agent_agent_proto_msgTypes[7] | |||||
| if protoimpl.UnsafeEnabled && x != nil { | |||||
| ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | |||||
| if ms.LoadMessageInfo() == nil { | |||||
| ms.StoreMessageInfo(mi) | |||||
| } | |||||
| return ms | |||||
| } | |||||
| return mi.MessageOf(x) | |||||
| } | |||||
| // Deprecated: Use PingResp.ProtoReflect.Descriptor instead. | |||||
| func (*PingResp) Descriptor() ([]byte, []int) { | |||||
| return file_pkgs_grpc_agent_agent_proto_rawDescGZIP(), []int{7} | |||||
| } | |||||
| var File_pkgs_grpc_agent_agent_proto protoreflect.FileDescriptor | var File_pkgs_grpc_agent_agent_proto protoreflect.FileDescriptor | ||||
| var file_pkgs_grpc_agent_agent_proto_rawDesc = []byte{ | var file_pkgs_grpc_agent_agent_proto_rawDesc = []byte{ | ||||
| @@ -415,26 +500,32 @@ var file_pkgs_grpc_agent_agent_proto_rawDesc = []byte{ | |||||
| 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x50, 0x6c, 0x61, 0x6e, | 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x50, 0x6c, 0x61, 0x6e, | ||||
| 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x50, 0x6c, 0x61, 0x6e, 0x49, 0x44, | 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x50, 0x6c, 0x61, 0x6e, 0x49, 0x44, | ||||
| 0x12, 0x1a, 0x0a, 0x08, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, | 0x12, 0x1a, 0x0a, 0x08, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, | ||||
| 0x28, 0x09, 0x52, 0x08, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x2a, 0x37, 0x0a, 0x14, | |||||
| 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, | |||||
| 0x54, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x45, 0x4f, 0x46, 0x10, 0x00, 0x12, 0x08, 0x0a, | |||||
| 0x04, 0x44, 0x61, 0x74, 0x61, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x65, 0x6e, 0x64, 0x41, | |||||
| 0x72, 0x67, 0x73, 0x10, 0x02, 0x32, 0xe1, 0x01, 0x0a, 0x05, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, | |||||
| 0x36, 0x0a, 0x0c, 0x53, 0x65, 0x6e, 0x64, 0x49, 0x50, 0x46, 0x53, 0x46, 0x69, 0x6c, 0x65, 0x12, | |||||
| 0x0f, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, | |||||
| 0x1a, 0x11, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x49, 0x50, 0x46, 0x53, 0x46, 0x69, 0x6c, 0x65, 0x52, | |||||
| 0x65, 0x73, 0x70, 0x22, 0x00, 0x28, 0x01, 0x12, 0x33, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x49, 0x50, | |||||
| 0x46, 0x53, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x0f, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x50, 0x46, 0x53, | |||||
| 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x61, | |||||
| 0x74, 0x61, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x22, 0x00, 0x30, 0x01, 0x12, 0x34, 0x0a, 0x0a, | |||||
| 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x11, 0x2e, 0x53, 0x74, 0x72, | |||||
| 0x65, 0x61, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x1a, 0x0f, 0x2e, | |||||
| 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, | |||||
| 0x28, 0x01, 0x12, 0x35, 0x0a, 0x0b, 0x46, 0x65, 0x74, 0x63, 0x68, 0x53, 0x74, 0x72, 0x65, 0x61, | |||||
| 0x6d, 0x12, 0x0f, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, | |||||
| 0x65, 0x71, 0x1a, 0x11, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x50, | |||||
| 0x61, 0x63, 0x6b, 0x65, 0x74, 0x22, 0x00, 0x30, 0x01, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x3b, 0x61, | |||||
| 0x67, 0x65, 0x6e, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, | |||||
| 0x28, 0x09, 0x52, 0x08, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x22, 0x29, 0x0a, 0x07, | |||||
| 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x12, 0x1e, 0x0a, 0x0a, 0x46, 0x72, 0x6f, 0x6d, 0x4e, | |||||
| 0x6f, 0x64, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x46, 0x72, 0x6f, | |||||
| 0x6d, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x44, 0x22, 0x0a, 0x0a, 0x08, 0x50, 0x69, 0x6e, 0x67, 0x52, | |||||
| 0x65, 0x73, 0x70, 0x2a, 0x37, 0x0a, 0x14, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x44, 0x61, 0x74, | |||||
| 0x61, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x45, | |||||
| 0x4f, 0x46, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x10, 0x01, 0x12, 0x0c, | |||||
| 0x0a, 0x08, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x72, 0x67, 0x73, 0x10, 0x02, 0x32, 0x80, 0x02, 0x0a, | |||||
| 0x05, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x36, 0x0a, 0x0c, 0x53, 0x65, 0x6e, 0x64, 0x49, 0x50, | |||||
| 0x46, 0x53, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x0f, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x61, 0x74, | |||||
| 0x61, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x1a, 0x11, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x49, 0x50, | |||||
| 0x46, 0x53, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x28, 0x01, 0x12, 0x33, | |||||
| 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x49, 0x50, 0x46, 0x53, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x0f, 0x2e, | |||||
| 0x47, 0x65, 0x74, 0x49, 0x50, 0x46, 0x53, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x0f, | |||||
| 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x61, 0x74, 0x61, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x22, | |||||
| 0x00, 0x30, 0x01, 0x12, 0x34, 0x0a, 0x0a, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x72, 0x65, 0x61, | |||||
| 0x6d, 0x12, 0x11, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x50, 0x61, | |||||
| 0x63, 0x6b, 0x65, 0x74, 0x1a, 0x0f, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x72, 0x65, 0x61, | |||||
| 0x6d, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x28, 0x01, 0x12, 0x35, 0x0a, 0x0b, 0x46, 0x65, 0x74, | |||||
| 0x63, 0x68, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x0f, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, | |||||
| 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, 0x53, 0x74, 0x72, 0x65, | |||||
| 0x61, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x22, 0x00, 0x30, 0x01, | |||||
| 0x12, 0x1d, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x08, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, | |||||
| 0x65, 0x71, 0x1a, 0x09, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x42, | |||||
| 0x09, 0x5a, 0x07, 0x2e, 0x3b, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, | |||||
| 0x6f, 0x33, | |||||
| } | } | ||||
| var ( | var ( | ||||
| @@ -450,7 +541,7 @@ func file_pkgs_grpc_agent_agent_proto_rawDescGZIP() []byte { | |||||
| } | } | ||||
| var file_pkgs_grpc_agent_agent_proto_enumTypes = make([]protoimpl.EnumInfo, 1) | var file_pkgs_grpc_agent_agent_proto_enumTypes = make([]protoimpl.EnumInfo, 1) | ||||
| var file_pkgs_grpc_agent_agent_proto_msgTypes = make([]protoimpl.MessageInfo, 6) | |||||
| var file_pkgs_grpc_agent_agent_proto_msgTypes = make([]protoimpl.MessageInfo, 8) | |||||
| var file_pkgs_grpc_agent_agent_proto_goTypes = []interface{}{ | var file_pkgs_grpc_agent_agent_proto_goTypes = []interface{}{ | ||||
| (StreamDataPacketType)(0), // 0: StreamDataPacketType | (StreamDataPacketType)(0), // 0: StreamDataPacketType | ||||
| (*FileDataPacket)(nil), // 1: FileDataPacket | (*FileDataPacket)(nil), // 1: FileDataPacket | ||||
| @@ -459,6 +550,8 @@ var file_pkgs_grpc_agent_agent_proto_goTypes = []interface{}{ | |||||
| (*StreamDataPacket)(nil), // 4: StreamDataPacket | (*StreamDataPacket)(nil), // 4: StreamDataPacket | ||||
| (*SendStreamResp)(nil), // 5: SendStreamResp | (*SendStreamResp)(nil), // 5: SendStreamResp | ||||
| (*FetchStreamReq)(nil), // 6: FetchStreamReq | (*FetchStreamReq)(nil), // 6: FetchStreamReq | ||||
| (*PingReq)(nil), // 7: PingReq | |||||
| (*PingResp)(nil), // 8: PingResp | |||||
| } | } | ||||
| var file_pkgs_grpc_agent_agent_proto_depIdxs = []int32{ | var file_pkgs_grpc_agent_agent_proto_depIdxs = []int32{ | ||||
| 0, // 0: FileDataPacket.Type:type_name -> StreamDataPacketType | 0, // 0: FileDataPacket.Type:type_name -> StreamDataPacketType | ||||
| @@ -467,12 +560,14 @@ var file_pkgs_grpc_agent_agent_proto_depIdxs = []int32{ | |||||
| 3, // 3: Agent.GetIPFSFile:input_type -> GetIPFSFileReq | 3, // 3: Agent.GetIPFSFile:input_type -> GetIPFSFileReq | ||||
| 4, // 4: Agent.SendStream:input_type -> StreamDataPacket | 4, // 4: Agent.SendStream:input_type -> StreamDataPacket | ||||
| 6, // 5: Agent.FetchStream:input_type -> FetchStreamReq | 6, // 5: Agent.FetchStream:input_type -> FetchStreamReq | ||||
| 2, // 6: Agent.SendIPFSFile:output_type -> SendIPFSFileResp | |||||
| 1, // 7: Agent.GetIPFSFile:output_type -> FileDataPacket | |||||
| 5, // 8: Agent.SendStream:output_type -> SendStreamResp | |||||
| 4, // 9: Agent.FetchStream:output_type -> StreamDataPacket | |||||
| 6, // [6:10] is the sub-list for method output_type | |||||
| 2, // [2:6] is the sub-list for method input_type | |||||
| 7, // 6: Agent.Ping:input_type -> PingReq | |||||
| 2, // 7: Agent.SendIPFSFile:output_type -> SendIPFSFileResp | |||||
| 1, // 8: Agent.GetIPFSFile:output_type -> FileDataPacket | |||||
| 5, // 9: Agent.SendStream:output_type -> SendStreamResp | |||||
| 4, // 10: Agent.FetchStream:output_type -> StreamDataPacket | |||||
| 8, // 11: Agent.Ping:output_type -> PingResp | |||||
| 7, // [7:12] is the sub-list for method output_type | |||||
| 2, // [2:7] is the sub-list for method input_type | |||||
| 2, // [2:2] is the sub-list for extension type_name | 2, // [2:2] is the sub-list for extension type_name | ||||
| 2, // [2:2] is the sub-list for extension extendee | 2, // [2:2] is the sub-list for extension extendee | ||||
| 0, // [0:2] is the sub-list for field type_name | 0, // [0:2] is the sub-list for field type_name | ||||
| @@ -556,6 +651,30 @@ func file_pkgs_grpc_agent_agent_proto_init() { | |||||
| return nil | return nil | ||||
| } | } | ||||
| } | } | ||||
| file_pkgs_grpc_agent_agent_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { | |||||
| switch v := v.(*PingReq); i { | |||||
| case 0: | |||||
| return &v.state | |||||
| case 1: | |||||
| return &v.sizeCache | |||||
| case 2: | |||||
| return &v.unknownFields | |||||
| default: | |||||
| return nil | |||||
| } | |||||
| } | |||||
| file_pkgs_grpc_agent_agent_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { | |||||
| switch v := v.(*PingResp); i { | |||||
| case 0: | |||||
| return &v.state | |||||
| case 1: | |||||
| return &v.sizeCache | |||||
| case 2: | |||||
| return &v.unknownFields | |||||
| default: | |||||
| return nil | |||||
| } | |||||
| } | |||||
| } | } | ||||
| type x struct{} | type x struct{} | ||||
| out := protoimpl.TypeBuilder{ | out := protoimpl.TypeBuilder{ | ||||
| @@ -563,7 +682,7 @@ func file_pkgs_grpc_agent_agent_proto_init() { | |||||
| GoPackagePath: reflect.TypeOf(x{}).PkgPath(), | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), | ||||
| RawDescriptor: file_pkgs_grpc_agent_agent_proto_rawDesc, | RawDescriptor: file_pkgs_grpc_agent_agent_proto_rawDesc, | ||||
| NumEnums: 1, | NumEnums: 1, | ||||
| NumMessages: 6, | |||||
| NumMessages: 8, | |||||
| NumExtensions: 0, | NumExtensions: 0, | ||||
| NumServices: 1, | NumServices: 1, | ||||
| }, | }, | ||||
| @@ -40,11 +40,18 @@ message FetchStreamReq { | |||||
| string StreamID = 2; | string StreamID = 2; | ||||
| } | } | ||||
| message PingReq { | |||||
| int64 FromNodeID = 1; | |||||
| } | |||||
| message PingResp {} | |||||
| service Agent { | service Agent { | ||||
| rpc SendIPFSFile(stream FileDataPacket)returns(SendIPFSFileResp){} | rpc SendIPFSFile(stream FileDataPacket)returns(SendIPFSFileResp){} | ||||
| rpc GetIPFSFile(GetIPFSFileReq)returns(stream FileDataPacket){} | rpc GetIPFSFile(GetIPFSFileReq)returns(stream FileDataPacket){} | ||||
| rpc SendStream(stream StreamDataPacket)returns(SendStreamResp){} | rpc SendStream(stream StreamDataPacket)returns(SendStreamResp){} | ||||
| rpc FetchStream(FetchStreamReq)returns(stream StreamDataPacket){} | rpc FetchStream(FetchStreamReq)returns(stream StreamDataPacket){} | ||||
| rpc Ping(PingReq) returns(PingResp){} | |||||
| } | } | ||||
| @@ -25,6 +25,7 @@ const ( | |||||
| Agent_GetIPFSFile_FullMethodName = "/Agent/GetIPFSFile" | Agent_GetIPFSFile_FullMethodName = "/Agent/GetIPFSFile" | ||||
| Agent_SendStream_FullMethodName = "/Agent/SendStream" | Agent_SendStream_FullMethodName = "/Agent/SendStream" | ||||
| Agent_FetchStream_FullMethodName = "/Agent/FetchStream" | Agent_FetchStream_FullMethodName = "/Agent/FetchStream" | ||||
| Agent_Ping_FullMethodName = "/Agent/Ping" | |||||
| ) | ) | ||||
| // AgentClient is the client API for Agent service. | // AgentClient is the client API for Agent service. | ||||
| @@ -35,6 +36,7 @@ type AgentClient interface { | |||||
| GetIPFSFile(ctx context.Context, in *GetIPFSFileReq, opts ...grpc.CallOption) (Agent_GetIPFSFileClient, error) | GetIPFSFile(ctx context.Context, in *GetIPFSFileReq, opts ...grpc.CallOption) (Agent_GetIPFSFileClient, error) | ||||
| SendStream(ctx context.Context, opts ...grpc.CallOption) (Agent_SendStreamClient, error) | SendStream(ctx context.Context, opts ...grpc.CallOption) (Agent_SendStreamClient, error) | ||||
| FetchStream(ctx context.Context, in *FetchStreamReq, opts ...grpc.CallOption) (Agent_FetchStreamClient, error) | FetchStream(ctx context.Context, in *FetchStreamReq, opts ...grpc.CallOption) (Agent_FetchStreamClient, error) | ||||
| Ping(ctx context.Context, in *PingReq, opts ...grpc.CallOption) (*PingResp, error) | |||||
| } | } | ||||
| type agentClient struct { | type agentClient struct { | ||||
| @@ -177,6 +179,15 @@ func (x *agentFetchStreamClient) Recv() (*StreamDataPacket, error) { | |||||
| return m, nil | return m, nil | ||||
| } | } | ||||
| func (c *agentClient) Ping(ctx context.Context, in *PingReq, opts ...grpc.CallOption) (*PingResp, error) { | |||||
| out := new(PingResp) | |||||
| err := c.cc.Invoke(ctx, Agent_Ping_FullMethodName, in, out, opts...) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| return out, nil | |||||
| } | |||||
| // AgentServer is the server API for Agent service. | // AgentServer is the server API for Agent service. | ||||
| // All implementations must embed UnimplementedAgentServer | // All implementations must embed UnimplementedAgentServer | ||||
| // for forward compatibility | // for forward compatibility | ||||
| @@ -185,6 +196,7 @@ type AgentServer interface { | |||||
| GetIPFSFile(*GetIPFSFileReq, Agent_GetIPFSFileServer) error | GetIPFSFile(*GetIPFSFileReq, Agent_GetIPFSFileServer) error | ||||
| SendStream(Agent_SendStreamServer) error | SendStream(Agent_SendStreamServer) error | ||||
| FetchStream(*FetchStreamReq, Agent_FetchStreamServer) error | FetchStream(*FetchStreamReq, Agent_FetchStreamServer) error | ||||
| Ping(context.Context, *PingReq) (*PingResp, error) | |||||
| mustEmbedUnimplementedAgentServer() | mustEmbedUnimplementedAgentServer() | ||||
| } | } | ||||
| @@ -204,6 +216,9 @@ func (UnimplementedAgentServer) SendStream(Agent_SendStreamServer) error { | |||||
| func (UnimplementedAgentServer) FetchStream(*FetchStreamReq, Agent_FetchStreamServer) error { | func (UnimplementedAgentServer) FetchStream(*FetchStreamReq, Agent_FetchStreamServer) error { | ||||
| return status.Errorf(codes.Unimplemented, "method FetchStream not implemented") | return status.Errorf(codes.Unimplemented, "method FetchStream not implemented") | ||||
| } | } | ||||
| func (UnimplementedAgentServer) Ping(context.Context, *PingReq) (*PingResp, error) { | |||||
| return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented") | |||||
| } | |||||
| func (UnimplementedAgentServer) mustEmbedUnimplementedAgentServer() {} | func (UnimplementedAgentServer) mustEmbedUnimplementedAgentServer() {} | ||||
| // UnsafeAgentServer may be embedded to opt out of forward compatibility for this service. | // UnsafeAgentServer may be embedded to opt out of forward compatibility for this service. | ||||
| @@ -311,13 +326,36 @@ func (x *agentFetchStreamServer) Send(m *StreamDataPacket) error { | |||||
| return x.ServerStream.SendMsg(m) | return x.ServerStream.SendMsg(m) | ||||
| } | } | ||||
| func _Agent_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { | |||||
| in := new(PingReq) | |||||
| if err := dec(in); err != nil { | |||||
| return nil, err | |||||
| } | |||||
| if interceptor == nil { | |||||
| return srv.(AgentServer).Ping(ctx, in) | |||||
| } | |||||
| info := &grpc.UnaryServerInfo{ | |||||
| Server: srv, | |||||
| FullMethod: Agent_Ping_FullMethodName, | |||||
| } | |||||
| handler := func(ctx context.Context, req interface{}) (interface{}, error) { | |||||
| return srv.(AgentServer).Ping(ctx, req.(*PingReq)) | |||||
| } | |||||
| return interceptor(ctx, in, info, handler) | |||||
| } | |||||
| // Agent_ServiceDesc is the grpc.ServiceDesc for Agent service. | // Agent_ServiceDesc is the grpc.ServiceDesc for Agent service. | ||||
| // It's only intended for direct use with grpc.RegisterService, | // It's only intended for direct use with grpc.RegisterService, | ||||
| // and not to be introspected or modified (even as a copy) | // and not to be introspected or modified (even as a copy) | ||||
| var Agent_ServiceDesc = grpc.ServiceDesc{ | var Agent_ServiceDesc = grpc.ServiceDesc{ | ||||
| ServiceName: "Agent", | ServiceName: "Agent", | ||||
| HandlerType: (*AgentServer)(nil), | HandlerType: (*AgentServer)(nil), | ||||
| Methods: []grpc.MethodDesc{}, | |||||
| Methods: []grpc.MethodDesc{ | |||||
| { | |||||
| MethodName: "Ping", | |||||
| Handler: _Agent_Ping_Handler, | |||||
| }, | |||||
| }, | |||||
| Streams: []grpc.StreamDesc{ | Streams: []grpc.StreamDesc{ | ||||
| { | { | ||||
| StreamName: "SendIPFSFile", | StreamName: "SendIPFSFile", | ||||
| @@ -5,6 +5,7 @@ import ( | |||||
| "fmt" | "fmt" | ||||
| "io" | "io" | ||||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | |||||
| "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch" | "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch" | ||||
| "google.golang.org/grpc" | "google.golang.org/grpc" | ||||
| "google.golang.org/grpc/credentials/insecure" | "google.golang.org/grpc/credentials/insecure" | ||||
| @@ -209,6 +210,13 @@ func (c *Client) FetchStream(planID ioswitch.PlanID, streamID ioswitch.StreamID) | |||||
| }, nil | }, nil | ||||
| } | } | ||||
| func (c *Client) Ping(fromNodeID cdssdk.NodeID) error { | |||||
| _, err := c.cli.Ping(context.Background(), &PingReq{ | |||||
| FromNodeID: int64(fromNodeID), | |||||
| }) | |||||
| return err | |||||
| } | |||||
| func (c *Client) Close() { | func (c *Client) Close() { | ||||
| c.con.Close() | c.con.Close() | ||||
| } | } | ||||
| @@ -9,6 +9,10 @@ type NodeService interface { | |||||
| GetUserNodes(msg *GetUserNodes) (*GetUserNodesResp, *mq.CodeMessage) | GetUserNodes(msg *GetUserNodes) (*GetUserNodesResp, *mq.CodeMessage) | ||||
| GetNodes(msg *GetNodes) (*GetNodesResp, *mq.CodeMessage) | GetNodes(msg *GetNodes) (*GetNodesResp, *mq.CodeMessage) | ||||
| GetNodeConnectivities(msg *GetNodeConnectivities) (*GetNodeConnectivitiesResp, *mq.CodeMessage) | |||||
| UpdateNodeConnectivities(msg *UpdateNodeConnectivities) (*UpdateNodeConnectivitiesResp, *mq.CodeMessage) | |||||
| } | } | ||||
| // 查询用户可用的节点 | // 查询用户可用的节点 | ||||
| @@ -71,3 +75,52 @@ func (r *GetNodesResp) GetNode(id cdssdk.NodeID) *cdssdk.Node { | |||||
| func (client *Client) GetNodes(msg *GetNodes) (*GetNodesResp, error) { | func (client *Client) GetNodes(msg *GetNodes) (*GetNodesResp, error) { | ||||
| return mq.Request(Service.GetNodes, client.rabbitCli, msg) | return mq.Request(Service.GetNodes, client.rabbitCli, msg) | ||||
| } | } | ||||
| // 获取节点连通性信息 | |||||
| var _ = Register(Service.GetNodeConnectivities) | |||||
| type GetNodeConnectivities struct { | |||||
| mq.MessageBodyBase | |||||
| NodeIDs []cdssdk.NodeID `json:"nodeIDs"` | |||||
| } | |||||
| type GetNodeConnectivitiesResp struct { | |||||
| mq.MessageBodyBase | |||||
| Connectivities []cdssdk.NodeConnectivity `json:"nodes"` | |||||
| } | |||||
| func ReqGetNodeConnectivities(nodeIDs []cdssdk.NodeID) *GetNodeConnectivities { | |||||
| return &GetNodeConnectivities{ | |||||
| NodeIDs: nodeIDs, | |||||
| } | |||||
| } | |||||
| func RespGetNodeConnectivities(cons []cdssdk.NodeConnectivity) *GetNodeConnectivitiesResp { | |||||
| return &GetNodeConnectivitiesResp{ | |||||
| Connectivities: cons, | |||||
| } | |||||
| } | |||||
| func (client *Client) GetNodeConnectivities(msg *GetNodeConnectivities) (*GetNodeConnectivitiesResp, error) { | |||||
| return mq.Request(Service.GetNodeConnectivities, client.rabbitCli, msg) | |||||
| } | |||||
| // 批量更新节点连通性信息 | |||||
| var _ = Register(Service.UpdateNodeConnectivities) | |||||
| type UpdateNodeConnectivities struct { | |||||
| mq.MessageBodyBase | |||||
| Connectivities []cdssdk.NodeConnectivity `json:"connectivities"` | |||||
| } | |||||
| type UpdateNodeConnectivitiesResp struct { | |||||
| mq.MessageBodyBase | |||||
| } | |||||
| func ReqUpdateNodeConnectivities(cons []cdssdk.NodeConnectivity) *UpdateNodeConnectivities { | |||||
| return &UpdateNodeConnectivities{ | |||||
| Connectivities: cons, | |||||
| } | |||||
| } | |||||
| func RespUpdateNodeConnectivities() *UpdateNodeConnectivitiesResp { | |||||
| return &UpdateNodeConnectivitiesResp{} | |||||
| } | |||||
| func (client *Client) UpdateNodeConnectivities(msg *UpdateNodeConnectivities) (*UpdateNodeConnectivitiesResp, error) { | |||||
| return mq.Request(Service.UpdateNodeConnectivities, client.rabbitCli, msg) | |||||
| } | |||||
| @@ -1,6 +1,10 @@ | |||||
| package mq | package mq | ||||
| import ( | import ( | ||||
| "database/sql" | |||||
| "fmt" | |||||
| "github.com/jmoiron/sqlx" | |||||
| "gitlink.org.cn/cloudream/common/consts/errorcode" | "gitlink.org.cn/cloudream/common/consts/errorcode" | ||||
| "gitlink.org.cn/cloudream/common/pkgs/logger" | "gitlink.org.cn/cloudream/common/pkgs/logger" | ||||
| "gitlink.org.cn/cloudream/common/pkgs/mq" | "gitlink.org.cn/cloudream/common/pkgs/mq" | ||||
| @@ -46,3 +50,48 @@ func (svc *Service) GetNodes(msg *coormq.GetNodes) (*coormq.GetNodesResp, *mq.Co | |||||
| return mq.ReplyOK(coormq.NewGetNodesResp(nodes)) | return mq.ReplyOK(coormq.NewGetNodesResp(nodes)) | ||||
| } | } | ||||
| func (svc *Service) GetNodeConnectivities(msg *coormq.GetNodeConnectivities) (*coormq.GetNodeConnectivitiesResp, *mq.CodeMessage) { | |||||
| cons, err := svc.db.NodeConnectivity().BatchGetByFromNode(svc.db.SQLCtx(), msg.NodeIDs) | |||||
| if err != nil { | |||||
| logger.Warnf("batch get node connectivities by from node: %s", err.Error()) | |||||
| return nil, mq.Failed(errorcode.OperationFailed, "batch get node connectivities by from node failed") | |||||
| } | |||||
| return mq.ReplyOK(coormq.RespGetNodeConnectivities(cons)) | |||||
| } | |||||
| func (svc *Service) UpdateNodeConnectivities(msg *coormq.UpdateNodeConnectivities) (*coormq.UpdateNodeConnectivitiesResp, *mq.CodeMessage) { | |||||
| err := svc.db.DoTx(sql.LevelSerializable, func(tx *sqlx.Tx) error { | |||||
| // 只有发起节点和目的节点都存在,才能插入这条记录到数据库 | |||||
| allNodes, err := svc.db.Node().GetAllNodes(tx) | |||||
| if err != nil { | |||||
| return fmt.Errorf("getting all nodes: %w", err) | |||||
| } | |||||
| allNodeID := make(map[cdssdk.NodeID]bool) | |||||
| for _, node := range allNodes { | |||||
| allNodeID[node.NodeID] = true | |||||
| } | |||||
| var avaiCons []cdssdk.NodeConnectivity | |||||
| for _, con := range msg.Connectivities { | |||||
| if allNodeID[con.FromNodeID] && allNodeID[con.ToNodeID] { | |||||
| avaiCons = append(avaiCons, con) | |||||
| } | |||||
| } | |||||
| err = svc.db.NodeConnectivity().BatchUpdateOrCreate(tx, avaiCons) | |||||
| if err != nil { | |||||
| return fmt.Errorf("batch update or create node connectivities: %s", err) | |||||
| } | |||||
| return nil | |||||
| }) | |||||
| if err != nil { | |||||
| logger.Warn(err.Error()) | |||||
| return nil, mq.Failed(errorcode.OperationFailed, err.Error()) | |||||
| } | |||||
| return mq.ReplyOK(coormq.RespUpdateNodeConnectivities()) | |||||
| } | |||||