怎么结合呢?

我们先来回顾一下一致性哈希代码实现里面的结构

// Consistent holds the information about the members of the consistent hash circle.
type Consistent struct {mu sync.RWMutex // 读写锁,用于保护并发访问共享数据config         Config         // 存储配置信息hasher         Hasher         // 存储哈希器实例,直接从 config 复制过来sortedSet      []uint64       // 存储所有虚拟节点(通过成员名称+副本索引哈希得到)的哈希值,并保持升序排列。这是哈希环的骨架。partitionCount uint64         // 逻辑分区的总数,从 config.PartitionCount 转换而来loads          map[string]float64 // 存储每个真实成员当前的负载(即它拥有的逻辑分区数量)members        map[string]*Member // 存储所有真实成员的映射,键是成员的 String() 返回值,值是指向 Member 接口的指针partitions     map[int]*Member    // 核心映射:存储每个逻辑分区ID(0到PartitionCount-1)对应的真实成员(指向 Member 接口的指针)ring           map[uint64]*Member // 存储虚拟节点哈希值到真实成员的映射。它是 sortedSet 的补充,sortedSet 提供有序列表,ring 提供哈希值到成员的查找。
}

现在就是加入一个新的服务器后,会修改什么内容

func (c *Consistent) add(member Member) {for i := 0; i < c.config.ReplicationFactor; i++ {key := []byte(fmt.Sprintf("%s%d", member.String(), i))h := c.hasher.Sum64(key)c.ring[h] = &memberc.sortedSet = append(c.sortedSet, h)}// sort hashes ascendinglysort.Slice(c.sortedSet, func(i int, j int) bool {return c.sortedSet[i] < c.sortedSet[j]})// Storing member at this map is useful to find backup members of a partition.c.members[member.String()] = &member
}

我们每次增加服务器都会修改sortedset以及ring(虚拟节点和服务器的映射),但是如果我们能够得到服务器列表,那么是可以在本地构建sortedset和ring

分区和服务器映射需要存储到区块链中吗?不需要

它是通过确定性算法计算出来的:partitions 的内容是根据成员列表、虚拟节点哈希环和有界负载等参数,通过 distributePartitions() 方法计算得出的。这个过程是确定性的,只要输入(成员列表、配置)相同,输出(partitions 映射)也一定相同。
它与 sortedSet 是强关联的:partitions 映射的构建依赖于 sortedSet 和 ring。distributePartitions() 函数正是通过遍历 sortedSet 来为每个逻辑分区寻找归属成员的。
它是高频查询的关键:Consistent.LocateKey 方法的最后一步就是查询 partitions 映射。如果这个映射也需要从区块链上获取,那么每次键的定位都会变成一个慢速的区块链查询,这会彻底破坏系统的性能。

下面开始构建
链码
我们先创建一个链码
找一个位置存下下面的代码

package mainimport ("encoding/json""fmt""log"// "strconv"// "github.com/hyperledger/fabric-chaincode-go/shim""github.com/hyperledger/fabric-contract-api-go/contractapi"
)// ConsistentHashManager 链码的智能合约结构体
type ConsistentHashManager struct {contractapi.Contract
}// 成员列表键,用于在世界状态中存储成员列表
const membersKey = "all_members_key"// Member represents a member in the consistent hash ring.
// This is the same structure we used in our previous local Go program.
type Member struct {Name string `json:"name"`
}// InitLedger 初始化链码。
// 在这里,我们将创建一个空的成员列表并将其写入世界状态。
func (s *ConsistentHashManager) InitLedger(ctx contractapi.TransactionContextInterface) error {log.Printf("Initializing the ledger with an empty member list.")// 创建一个空成员列表members := []Member{}membersJSON, err := json.Marshal(members)if err != nil {return err}// 将空成员列表写入世界状态return ctx.GetStub().PutState(membersKey, membersJSON)
}// AddMember 添加一个新成员到哈希环中。
func (s *ConsistentHashManager) AddMember(ctx contractapi.TransactionContextInterface, memberName string) error {log.Printf("Attempting to add member: %s", memberName)// 从世界状态中读取当前成员列表membersJSON, err := ctx.GetStub().GetState(membersKey)if err != nil {return fmt.Errorf("failed to read from world state: %w", err)}// 如果列表不存在,创建一个空列表if membersJSON == nil {membersJSON = []byte("[]")}// 反序列化成员列表var members []Membererr = json.Unmarshal(membersJSON, &members)if err != nil {return fmt.Errorf("failed to unmarshal member list: %w", err)}// 检查成员是否已存在for _, member := range members {if member.Name == memberName {return fmt.Errorf("member '%s' already exists", memberName)}}// 添加新成员members = append(members, Member{Name: memberName})// 将更新后的列表序列化membersJSON, err = json.Marshal(members)if err != nil {return err}// 将更新后的列表写入世界状态return ctx.GetStub().PutState(membersKey, membersJSON)
}// RemoveMember 从哈希环中移除一个成员。
func (s *ConsistentHashManager) RemoveMember(ctx contractapi.TransactionContextInterface, memberName string) error {log.Printf("Attempting to remove member: %s", memberName)// 从世界状态中读取当前成员列表membersJSON, err := ctx.GetStub().GetState(membersKey)if err != nil {return fmt.Errorf("failed to read from world state: %w", err)}if membersJSON == nil {return fmt.Errorf("member list is empty, cannot remove '%s'", memberName)}// 反序列化成员列表var members []Membererr = json.Unmarshal(membersJSON, &members)if err != nil {return fmt.Errorf("failed to unmarshal member list: %w", err)}// 查找并移除成员found := falsefor i, member := range members {if member.Name == memberName {// 移除切片中的元素members = append(members[:i], members[i+1:]...)found = truebreak}}if !found {return fmt.Errorf("member '%s' not found", memberName)}// 将更新后的列表序列化membersJSON, err = json.Marshal(members)if err != nil {return err}// 将更新后的列表写入世界状态return ctx.GetStub().PutState(membersKey, membersJSON)
}// GetMembers 查询并返回当前所有成员。
func (s *ConsistentHashManager) GetMembers(ctx contractapi.TransactionContextInterface) ([]*Member, error) {log.Println("Querying for all members.")// 从世界状态中读取成员列表membersJSON, err := ctx.GetStub().GetState(membersKey)if err != nil {return nil, fmt.Errorf("failed to read from world state: %w", err)}if membersJSON == nil {log.Println("No members found, returning empty list.")return []*Member{}, nil}// 反序列化成员列表var members []Membererr = json.Unmarshal(membersJSON, &members)if err != nil {return nil, fmt.Errorf("failed to unmarshal member list: %w", err)}// 将 []Member 转换为 []*Member 以符合 contractapi 接口var result []*Memberfor i := range members {result = append(result, &members[i])}return result, nil
}func main() {chaincode, err := contractapi.NewChaincode(&ConsistentHashManager{})if err != nil {log.Panicf("Error creating consistent hash manager chaincode: %v", err)}if err := chaincode.Start(); err != nil {log.Panicf("Error starting consistent hash manager chaincode: %v", err)}
}

然后我们再运行

go mod init consistent-hash-manager # 你可以替换成你喜欢的模块名
go mod tidy

下面就可以去部署网络,部署链码

./network.sh deployCC -ccn consistent-hash-manager -ccp ../chaincode/ConsistentHashManager -ccv 1.0 -ccl go -c mychannel

接着我们运行我们的客户端代码

package mainimport ("bytes""crypto/x509""encoding/json""fmt""log""os""path"// "time""github.com/hyperledger/fabric-gateway/pkg/client""github.com/hyperledger/fabric-gateway/pkg/identity""google.golang.org/grpc""google.golang.org/grpc/credentials"
)// 定义链码名称、通道名称和网络配置路径
const (mspID       = "Org1MSP"cryptoPath  = "../../test-network/organizations/peerOrganizations/org1.example.com"certPath    = cryptoPath + "/users/User1@org1.example.com/msp/signcerts"keyPath     = cryptoPath + "/users/User1@org1.example.com/msp/keystore"tlsCertPath = cryptoPath + "/peers/peer0.org1.example.com/tls/ca.crt"peerEndpoint = "localhost:7051"         // Fabric Peer 的 Gateway 服务地址gatewayPeer  = "peer0.org1.example.com" // Gateway Peer 的主机名,用于 TLS 验证channelName   = "mychannel"chaincodeName = "consistent-hash-manager" // 使用我们自己的链码名称
)// Member 结构体,用于解析链码返回的成员列表
type Member struct {Name string `json:"name"`
}// newGrpcConnection 创建与 Gateway 服务器的 gRPC 连接
func newGrpcConnection() *grpc.ClientConn {log.Println("--> Creating gRPC client connection...")certificatePEM, err := os.ReadFile(tlsCertPath)if err != nil {log.Fatalf("Failed to read TLS certificate file at %s: %v", tlsCertPath, err)}certPool := x509.NewCertPool()if !certPool.AppendCertsFromPEM(certificatePEM) {log.Fatalf("Failed to append TLS CA certificate to pool")}transportCredentials := credentials.NewClientTLSFromCert(certPool, gatewayPeer)connection, err := grpc.NewClient(peerEndpoint, grpc.WithTransportCredentials(transportCredentials), grpc.WithBlock())if err != nil {log.Fatalf("Failed to create gRPC connection: %v", err)}log.Println("--> gRPC client connection created successfully.")return connection
}// newIdentity 为 Gateway 连接创建一个客户端身份 (X.509 证书)
func newIdentity() *identity.X509Identity {log.Println("--> Creating new client identity...")certificatePEM, err := readFirstFile(certPath)if err != nil {log.Fatalf("Failed to read certificate file from %s: %v", certPath, err)}certificate, err := identity.CertificateFromPEM(certificatePEM)if err != nil {log.Fatalf("Failed to parse identity certificate: %v", err)}id, err := identity.NewX509Identity(mspID, certificate)if err != nil {log.Fatalf("Failed to create X.509 identity: %v", err)}log.Println("--> Client identity created successfully.")return id
}// newSign 创建一个函数,该函数使用私钥从消息摘要生成数字签名。
func newSign() identity.Sign {log.Println("--> Creating new private key signer...")privateKeyPEM, err := readFirstFile(keyPath)if err != nil {log.Fatalf("Failed to read private key file from %s: %v", keyPath, err)}privateKey, err := identity.PrivateKeyFromPEM(privateKeyPEM)if err != nil {log.Fatalf("Failed to parse private key: %v", err)}sign, err := identity.NewPrivateKeySign(privateKey)if err != nil {log.Fatalf("Failed to create private key signer: %v", err)}log.Println("--> Private key signer created successfully.")return sign
}// readFirstFile 从指定目录中读取第一个文件
func readFirstFile(dirPath string) ([]byte, error) {dir, err := os.Open(dirPath)if err != nil {return nil, fmt.Errorf("failed to open directory %s: %w", dirPath, err)}defer dir.Close()fileNames, err := dir.Readdirnames(1)if err != nil {return nil, fmt.Errorf("failed to read file names from directory %s: %w", dirPath, err)}if len(fileNames) == 0 {return nil, fmt.Errorf("no files found in directory: %s", dirPath)}filePath := path.Join(dirPath, fileNames[0])fileContent, err := os.ReadFile(filePath)if err != nil {return nil, fmt.Errorf("failed to read file %s: %w", filePath, err)}return fileContent, nil
}// formatJSON 格式化 JSON 数据,使其更易读
func formatJSON(data []byte) string {if len(data) == 0 {return "[]" // 如果数据为空,返回一个空的 JSON 数组字符串}var prettyJSON bytes.Bufferif err := json.Indent(&prettyJSON, data, "", "  "); err != nil {log.Printf("Warning: Failed to parse JSON, returning raw data. Error: %v", err)return string(data)}return prettyJSON.String()
}func main() {// 1. 创建 gRPC 客户端连接clientConnection := newGrpcConnection()defer clientConnection.Close()// 2. 创建客户端身份和签名函数id := newIdentity()sign := newSign()// 3. 连接 Fabric Gatewaygw, err := client.Connect(id,client.WithSign(sign),client.WithClientConnection(clientConnection),)if err != nil {log.Fatalf("Failed to connect to Gateway: %v", err)}defer gw.Close()// 获取网络和合约对象network := gw.GetNetwork(channelName)contract := network.GetContract(chaincodeName)// memberNameToAdd := "node-a"// // 4. 调用 AddMember 提交事务// log.Printf("\n--> 提交 AddMember 事务以添加 '%s'...", memberNameToAdd)// _, err = contract.SubmitTransaction("AddMember", memberNameToAdd)// if err != nil {// 	log.Fatalf("Failed to submit AddMember transaction: %v", err)// }// log.Printf("成员 '%s' 添加成功。", memberNameToAdd)// memberNameToAdd2 := "node-b"// // 4. 调用 AddMember 提交事务// log.Printf("\n--> 提交 AddMember 事务以添加 '%s'...", memberNameToAdd2)// _, err = contract.SubmitTransaction("AddMember", memberNameToAdd2)// if err != nil {// 	log.Fatalf("Failed to submit AddMember transaction: %v", err)// }// log.Printf("成员 '%s' 添加成功。", memberNameToAdd2)// 5. 调用 GetMembers 查询当前成员列表log.Println("\n--> 查询所有成员...")result, err := contract.EvaluateTransaction("GetMembers")if err != nil {log.Fatalf("Failed to evaluate GetMembers transaction: %v", err)}log.Printf("当前成员列表:\n%s", formatJSON(result))// // 6. 调用 RemoveMember 提交事务// log.Printf("\n--> 提交 RemoveMember 事务以移除 '%s'...", memberNameToAdd)// _, err = contract.SubmitTransaction("RemoveMember", memberNameToAdd)// if err != nil {// 	log.Fatalf("Failed to submit RemoveMember transaction: %v", err)// }// log.Printf("成员 '%s' 移除成功。", memberNameToAdd)// // 7. 再次调用 GetMembers 查询以确认移除// log.Println("\n--> 再次查询所有成员以确认移除...")// result, err = contract.EvaluateTransaction("GetMembers")// if err != nil {// 	log.Fatalf("Failed to evaluate GetMembers transaction: %v", err)// }// log.Printf("移除后成员列表:\n%s", formatJSON(result))// log.Println("\n所有操作已完成。")
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/917561.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/917561.shtml
英文地址,请注明出处:http://en.pswp.cn/news/917561.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用yolo11训练智慧医疗-孤独症儿童行为检测数据集VOC+YOLO格式7295张34类别步骤和流程

【数据集介绍】数据集中有很多增强图片&#xff0c;也有很多视频连续截取图片请查看图片预览数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件)图片数量(jpg文件个数)&#xff1a;…

vim 组件 使用pysocket进行sock连接

vim组件实现 以下是使用 Vim 插件架构实现 Python Socket 客户端的完整方案&#xff0c;支持集成到 Vim 控件并实现双向通信&#xff1a; ~/.vim/plugin/socket_client.vim" 定义全局命令和快捷键 command! -nargs* SocketConnect call s:StartSocketClient(<f-args>…

FFmpeg+javacpp中纯音频播放

FFmpegjavacpp中纯音频播放1. Java Sound播放2、整合音频信息AudioInfo3、添加ExecutorService执行播放FFmpegjavacppjavacv使用 FFmpegjavacpp中FFmpegFrameGrabber FFmpegjavacpp中仿ffplay播放 JavaCV 1.5.12 API JavaCPP Presets for FFmpeg 7.1.1-1.5.12 API1. Java Soun…

洛谷P1036 [NOIP 2002 普及组] 选数

P1036 [NOIP 2002 普及组] 选数 题目描述 已知 nnn 个整数 x1,x2,⋯ ,xnx_1,x_2,\cdots,x_nx1​,x2​,⋯,xn​&#xff0c;以及 111 个整数 kkk&#xff08;k<nk<nk<n&#xff09;。从 nnn 个整数中任选 kkk 个整数相加&#xff0c;可分别得到一系列的和。例如当 n4n…

Linux学习记录(八)文件共享

本文记录在Vmware中启用文件共享时的一些注意事项&#xff1a;1.提前安装vmware-tools&#xff0c;可以通过Vmware的虚拟机菜单栏中拿到文件&#xff0c;然后直接运行vmware-install.pl文件进行安装&#xff1b;也可以通过指令sudo apt-get install open-vm-tools进行安装。推荐…

洛谷 火烧赤壁 差分/贪心

题目背景曹操平定北方以后&#xff0c;公元 208 年&#xff0c;率领大军南下&#xff0c;进攻刘表。他的人马还没有到荆州&#xff0c;刘表已经病死。他的儿子刘琮听到曹军声势浩大&#xff0c;吓破了胆&#xff0c;先派人求降了。孙权任命周瑜为都督&#xff0c;拨给他三万水军…

Linux 用户与组管理全解析

Linux 用户与组管理一、用户和组的基本概念 1. 用户账号类型 超级用户&#xff08;root&#xff09;&#xff1a;默认拥有系统最高权限&#xff08;UID0&#xff09;&#xff0c;仅建议用于系统管理与维护&#xff0c;日常操作应使用普通用户。普通用户&#xff1a;由管理员创建…

开疆智能ModbusTCP转Profient网关连接ER机器人配置案例

本案例时西门子1200PLC通过ModbusTCP转Profinet网关连接埃斯顿机器人的配置案例&#xff0c;网关作为ModbusTCP的客户端连接机器人。配置过程&#xff1a;首先打开机器人通讯手册。查询机器人支持的功能码及默认IP和端口号打开网关配置软件“Gateway Configuration Studio”新建…

Docker换源加速(更换镜像源)详细教程(2025.3最新可用镜像,全网最详细)

文章目录前言可用镜像源汇总换源方法1-临时换源换源方法2-永久换源&#xff08;推荐&#xff09;常见问题及对应解决方案1.换源后&#xff0c;可以成功pull&#xff0c;但是search会出错补充1.如何测试镜像源是否可用2.Docker内的Linux换源教程换源速通版&#xff08;可以直接无…

机器学习【三】SVM

本文系统介绍了支持向量机(SVM)的理论与实践。理论部分首先区分了线性可分与不可分问题&#xff0c;阐述了SVM通过寻找最优超平面实现分类的核心思想&#xff0c;包括支持向量、间隔最大化等关键概念。详细讲解了硬间隔与软间隔SVM的数学原理&#xff0c;以及核函数(线性核、多…

DevOps平台大比拼:Gitee、Jenkins与CircleCI如何选型?

DevOps平台大比拼&#xff1a;Gitee、Jenkins与CircleCI如何选型&#xff1f; 在数字化转型浪潮席卷全球的当下&#xff0c;DevOps已成为企业提升研发效能的关键引擎。面对市场上纷繁复杂的DevOps工具链&#xff0c;如何选择最适合自身业务需求的平台成为技术决策者的重要课题。…

开源医院信息管理系统:基于若依框架的智慧医疗解决方案

引言在数字化浪潮的推动下&#xff0c;医疗行业正加速向信息化、智能化转型。医院信息管理系统&#xff08;HIS&#xff09;作为医疗管理的核心工具&#xff0c;直接影响医院的运营效率和服务质量。近期&#xff0c;一款基于 若依框架 Vue 的开源医院管理系统&#xff08;hosp…

我的世界进阶模组开发教程——附魔(2)

EnchantmentHelper 类详解 EnchantmentHelper 是 Minecraft 中处理物品附魔逻辑的核心工具类,提供附魔的存储、查询、计算和应用等功能。以下是对其字段和方法的逐行详细解释: 关键字段 private static final String TAG_ENCH_ID = "id"; // NBT标签键:附…

深度学习零基础入门(4)-卷积神经网络架构

许久不见~ 本节我们延续上一节的话题来看看卷积神经网络的架构&#xff0c;看看具体的卷积、池化等操作卷积神经网络详解&#xff1a;从基础操作到整体架构 一、卷积操作&#xff1a;特征提取的核心 卷积是卷积神经网络&#xff08;CNN&#xff09;的核心操作&#xff0c;灵感来…

C语言的控制语句

C的控制语句 控制语句是C语言中用于控制程序执行流程的结构。通过控制语句,可以根据条件执行不同的代码块,或者重复执行某些操作,从而实现复杂的逻辑和功能。掌握控制语句是编写有效和高效C程序的关键。 1 条件控制 条件控制语句用于根据某些条件来决定程序的执行路径。C语…

Mac电脑基本功能快捷键

1. 个性化桌面 将喜爱照片添加为桌面墙纸。前往“系统设置”&#xff0c;然后点按边栏中的“墙纸”。点按“添加照片”&#xff0c;然后从文件或“照片”App选取一张照片。 2. 截屏 按下键盘上的Shift &#xfffc; Command ⌘ 5&#xff0c;然后选取捕捉整个屏幕、App窗口或…

微算法科技(NASDAQ: MLGO)开发量子边缘检测算法,为实时图像处理与边缘智能设备提供了新的解决方案

图像边缘检测是计算机视觉的核心任务&#xff0c;传统算法&#xff08;如 Sobel、Canny&#xff09;依赖梯度计算与阈值分割&#xff0c;在处理高分辨率、复杂纹理图像时面临计算效率瓶颈。随着量子计算技术的发展&#xff0c;利用量子态叠加与并行处理特性&#xff0c;微算法科…

断点续传Demo实现

基于我们的DownloadManager.swift代码&#xff0c;让我详细解释断点续传需要实现的核心功能&#xff1a; 断点续传的核心实现要素 1. 后台会话配置 private func setupBackgroundSession() {let config URLSessionConfiguration.background(withIdentifier: "com.test.do…

《Leetcode》-面试题-hot100-子串

题目列表 560. 和为K的子数组 中等难度 leetcode链接 239 滑动窗口最大值 困难难度 leetcode链接 76 最小覆盖子串 困难难度 leetcode链接 题目 &#xff08;1&#xff09;和为K的子数组 给你一个整数数组 nums 和一个整数 k &#xff0c;请你统计并返回 该数组中和为 …

点击弹框以外的区域关闭弹框

在 Vue 3 中&#xff0c;如果你想判断点击的目标是否在弹框内&#xff0c;可以通过以下步骤实现。这里我们将使用 ref 来引用弹框组件&#xff0c;并在点击事件中进行判断。 示例代码 1. 创建弹框子组件 首先&#xff0c;创建一个名为 Modal.vue 的子组件。 <!-- Modal.vue …