在分布式系统中,图存储的问题

在分布式系统中,图存储的问题。

在项目中,需要多个代理服务器共享一个用户行为访问图。
我们通过多个代理服务器共享一个redis数据库中存储的图,来实现多个代理服务器图同步的问题。文章解决的问题有两个:

  1. 怎样通过redis存储图数据结构?
    通过将图中的节点转化为json格式的string数据,来实现图中*Node节点的存储。从redis中取出数据时,将string数据解码为*Node节点
  2. 怎样解决图中节点的子节点在各个代理服务器上的地址不同的问题
    通过id来唯一标识节点及其子节点。在服务器上重构图模型时,通过节点id映射节点地址。

在单一服务器上存储图

在同一个代理中实现用户访问图很容易。只需存储当前节点,以及子节点的地址即可。

type Node struct {
	URL      string
	ToURL    map[string]int // 用于记录当前节点可以访问到的url节点,以及已经访问的次数。
	Children []*Node  // 用于记录当前节点的子节点地址
}

然而,因为在不同的代理服务器(或不同的进程)中,内存地址是本地的,无法跨进程或跨服务器共享。

当在Redis这样的中心化数据存储中保存数据时,不能存储内存地址,因为这些地址对于其他系统是没有意义的。

解决这个问题的方法是在序列化节点时不存储内存地址,而是存储能够唯一标识子节点的信息。

分布式系统上存储图

为了解决这个问题,我们为每个节点引入一个id,通过id唯一的表示一个图中的节点,并记录其子节点。

type Node struct {
	ID       int
	URL      string
	ToURL    map[string]int // 用于记录当前节点可以访问到的url节点,以及已经访问的次数。
	Children map[int]*Node  // 用于记录当前节点的子节点id以及对应的Node值。
}

在结构中,通过节点的id唯一的标识一个节点,并通过Children中的id记录当前节点的子节点。

为了能够通过id访问子Node节点,需要通过map[int]*Node来找出子节点id对应的*Node节点。

前边已经提到,内存地址对于不同的服务器来说是没有意义的,因此在redis中存储数据时,我们不用存储map[int]*Node。只需存储子节点对应的id。

图中节点转换为json字符串

我们通过redis存储Node节点。键为id,值为Node数据结构经处理后转换为的json字符串。(redis无法存储复杂的数据结构,因此需要把图中节点转化为json字符串)

通过以下函数将map[int]*Node转化为[]int即子节点。

func SerializeNode(node *Node) (string, error) {
	// 创建一个临时结构体来存储节点数据,不包含子节点指针
	type TempNode struct {
		ID       int
		URL      string
		ToURL    map[string]int
		Children []int
	}

	// 创建一个子节点ID列表
	childIDs := make([]int, 0, len(node.Children))
	for id := range node.Children {
		childIDs = append(childIDs, id)
	}

	// 使用临时结构体来序列化
	tempNode := TempNode{
		ID:       node.ID,
		URL:      node.URL,
		ToURL:    node.ToURL,
		Children: childIDs,
	}

	nodeJSON, err := json.Marshal(tempNode) 
	if err != nil {
		return "", err
	}
	return string(nodeJSON), nil
}

现在已经成功的在redis中通过id唯一的标识一个节点及其子节点了。值得注意的是,我们通过json中的Marshal函数将Node数据结构转换为了json字符串,因此在读取时,需要将json字符串还原为Node节点。

解码json字符串为图中节点

下面我们编写一个反序列化函数,来将json字符串重新转化为Node节点

func DeserializeNode(nodeJSON string) (*Node, error) {
	type TempNode struct {
		FatherID int
		ID       int
		URL      string
		ToURL    map[string]int
		Children []int
	}

	var tempNode TempNode
	err := json.Unmarshal([]byte(nodeJSON), &tempNode)
	if err != nil {
		return nil, err
	}

	// 创建新的Node实例
	node := &Node{
		FatherID: tempNode.FatherID,
		ID:       tempNode.ID,
		URL:      tempNode.URL,
		ToURL:    tempNode.ToURL,
		Children: make(map[int]*Node),
	}
	return node, nil
}

通过json中的Unmarshal函数我们将从redis中提取的nodeJSON字符串还原为Node格式。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值