网络存储协议(基于go实例)原创
金蝶云社区-仲玄
仲玄
1人赞赏了该文章 245次浏览 未经作者许可,禁止转载编辑于2023年12月27日 18:21:40

主要内容

一.网络存储是什么?

二.iSCSI是什么?

三.RDMA是什么?

四.NVME-oF是什么?


一.网络存储是什么?

网络存储是一种将存储资源连接到网络上,以便多台计算机可以共享和访问这些存储资源的技术。网络存储可以是基于本地网络的存储(如局域网),也可以是基于广域网的存储(如云存储)。

网络存储通常使用网络文件系统(NFS)或存储区域网络(SAN)等协议来实现存储资源的共享和访问。网络存储具有高可用性、可扩展性和灵活性等优点,可以满足不同场景下对存储资源的需求。


二.iSCSI是什么?

iSCSI(Internet Small Computer System Interface)是一种基于网络的存储协议,它可以将远程存储设备映射为本地磁盘,使得计算机可以像访问本地磁盘一样访问远程存储设备。iSCSI协议可以在以太网、光纤通道等网络上运行,它可以在计算机和存储设备之间建立虚拟的SCSI总线,从而实现数据的传输和管理。iSCSI协议可以简化存储系统的管理和部署,提高存储系统的可靠性和性能。


iSCSI 可以运行在用户态或内核态。在 Linux 系统中,iSCSI 的实现通常是使用内核模块,即 iscsi_tcp.ko 和 scsi_transport_iscsi.ko,它们负责处理 iSCSI 传输和 SCSI 命令处理。但是,还有一些用户态的 iSCSI 实现,如 open-iscsi,它使用用户态的 iSCSI 守护进程(iscsid)和 iSCSI 库(libiscsi)。用户态的 iSCSI 实现可以提供更灵活的配置和管理,但通常性能不如内核态的实现。


Go 语言实现的 iSCSI 库和工具可以用于构建和管理 iSCSI 存储系统,例如:


  • go-iscsi:一个基于 Go 语言实现的 iSCSI 库,支持创建和管理 iSCSI 目标和 LUN(逻辑单元)。它可以用于构建自己的 iSCSI 存储系统或集成到其他应用程序中。

  • iscsid:一个基于 Go 语言实现的 iSCSI 守护进程,可以用于创建和管理 iSCSI 目标和 LUN。它支持多个后端存储引擎,包括本地文件系统、Ceph、GlusterFS 等。

  • open-iscsi:一个开源的 iSCSI 实现,包括 iSCSI 客户端(initiator)和服务器端(target)。它基于 C 语言实现,但也有一些 Go 语言的工具和库可以用于与之交互。

  • dqlite:一个基于 Go 语言实现的分布式 SQLite 数据库,支持多节点复制和高可用性。它可以用于构建分布式 iSCSI 存储系统或其他分布式应用程序。


iscsid

iscsid 是 iSCSI 协议的守护进程,主要负责与 iSCSI initiator(客户端)通信,并将 iSCSI 请求转发给 SCSI 设备。在文件系统中,iscsid 通常是使用内核模块实现的。

iscsid 的内核模块通常由两部分组成:iscsi_tcp.ko 和 scsi_transport_iscsi.ko。其中,iscsi_tcp.ko 负责处理 iSCSI 的传输层协议,如建立和维护 TCP 连接、处理 iSCSI 登录和退出等。而 scsi_transport_iscsi.ko 则负责处理 iSCSI 的 SCSI 命令,如将 iSCSI 请求转发给 SCSI 设备、处理 SCSI 命令的响应等。

当 iscsid 启动时,它会加载 iscsi_tcp.ko 和 scsi_transport_iscsi.ko 内核模块,并创建对应的字符设备文件,如 /dev/sda、/dev/sdb 等。这些字符设备文件对应着 iSCSI target(服务器)上的 SCSI 设备,它们可以被当作普通的硬盘使用,可以格式化、挂载、读写等。


当 iSCSI initiator 发送 SCSI 命令时,iscsid 守护进程将命令转发给对应的字符设备文件,比如 /dev/sda。字符设备文件将命令转发给 SCSI 设备,如硬盘。SCSI 设备执行命令并返回结果。字符设备文件将结果返回给 iscsid 守护进程。iscsid 守护进程将结果返回给 iSCSI initiator。


以下是一个使用Go语言实现iSCSI客户端的简单示例程序:

package main 

import (        
"fmt" 
"github.com/gostor/gotgt/pkg/api" 
"github.com/gostor/gotgt/pkg/scsi" 
"github.com/gostor/gotgt/pkg/scsi/device" 
"github.com/gostor/gotgt/pkg/scsi/generic" 
"github.com/gostor/gotgt/pkg/scsi/transport" 
) 

func main() { 
    // 创建iSCSI客户端 
    client := transport.NewIscsiClient() 
    
    // 发起连接请求 
    conn, err := client.Connect("192.168.1.1", 3260, "iqn.202106.com.example:target") 
    if err != nil { 
        panic(err)
    } 
    defer conn.Close() 
    
    // 创建SCSI设备 
    d := device.NewSCSIDevice(1, 0, 0, 512, "test") 
    
    // 注册SCSI设备 
    api.RegisterDeviceHandler(d) 
    
    // 创建SCSI令 
    cdb := scsi.ReadCapacity10{} 
    cmd := generic.NewScsiCmd(scsi.ReadCapacity10Desc, cdb) 
    
    // 发送SCSI命令 
    resp := conn.SendCmd(cmd) 
    if resp.HasError() { 
        panic(resp.GetError()) 
     } 
     
     // 获取SCSI命令响应 
     buf := make([]byte, 8) 
     err = resp.UnmarshalData(buf) 
     if err != nil { 
         panic(err) 
     } 
     
     // 输出SCSI命令响应 
     fmt.Printf("%x\\\\n", buf) 
}


在此示例中,我们使用transport.NewIscsiClient函数创建了一个iSCSI客户端,并使用client.Connect函数发起了一个连接请求。

然后,我们创建了一个SCSI设备,并使用api.RegisterDeviceHandler函数将其注册到iSCSI客户端中。

接下来,我们创建了一个SCSI命令,并使用conn.SendCmd函数发送了该命令。

最后,我们使用resp.UnmarshalData函数获取了SCSI命令的响应,并将其转换为字节数组并打印出来。


三.RDMA是什么?

RDMA(Remote Direct Memory Access)是一种高性能、低延迟的网络传输技术,它可以让计算机之间直接访问彼此的内存,从而避免了数据在传输过程中的不必要的拷贝和上下文切换。RDMA 技术主要包括两种协议:InfiniBand 和 RoCE(RDMA over Converged Ethernet)。

在 RDMA 技术中,计算机之间的数据传输不再需要 CPU 的介入,而是直接通过网络适配器和内存控制器之间的 DMA 传输实现。这种方式可以大大减少 CPU 的负载和数据传输的延迟,从而提高传输的效率和性能。

RDMA 的工作流程通常包括以下几个步骤:

  1. 应用程序将数据写入内存中的缓冲区。

  2. 应用程序通过 RDMA API 发送数据传输请求。

  3. 网络适配器将数据传输请求发送到目标计算机。

  4. 目标计算机的网络适配器接收到数据传输请求。

  5. 目标计算机的内存控制器将请求的数据从内存中读取,并通过网络适配器将数据传输给源计算机的内存控制器。

  6. 源计算机的内存控制器将数据写入内存中的缓冲区,并通知应用程序传输完成。


RDMA 技术需要硬件和软件的支持,包括网络适配器、内存控制器、驱动程序和 RDMA API 等。此外,RDMA 技术也有一些限制和注意事项,比如需要保证数据传输的顺序和一致性,需要避免内存泄漏和数据溢出等。


Go-IPoIB库是一个Go语言实现的RDMA over IPoIB协议库,其底层原理主要是通过RDMA-CM API来实现RDMA通信。RDMA-CM是一种基于InfiniBand网络的RDMA通信管理器,它提供了一种通用的RDMA通信接口,可以在不同的RDMA网络上运行。RDMA-CM API包括RDMA-CM库和RDMA-CM头文件,可以在Linux系统上使用。

Go-IPoIB库提供了一种使用RDMA-CM API来实现RDMA over IPoIB协议的方法。在使用Go-IPoIB库时,需要使用ipoib.DialIB函数来创建RDMA连接,并指定本地和远程的IP地址和端口号等参数。然后,可以使用RDMA通信的Write和Read函数来进行数据的发送和接收。在数据传输过程中,Go-IPoIB库会自动封装和解封装IPoIB数据包,并使用RDMA-CM API来管理RDMA通信。


Go-IPoIB库的实现涉及到网络协议、硬件设备、系统调用等多个方面,比较复杂。使用Go-IPoIB库需要有一定的计算机网络和系统编程基础,并且需要详细了解RDMA-CM API的使用方法和相关的网络协议规范。


以下是一个简单的示例程序,实现了一个基本的RDMA写操作:

package main 

import ( 
"fmt" 
"log" 
"net" 
"time" 
"github.com/infiniband/go-ipoib" 
) 

func main() { 
    // 创建RDMA连接     
    conn, err := ipoib.DialIB("192.168.1.1", "ib0", "192.168.1.2", "ib0", 1024) 
    if err != nil { 
        log.Fatal(err) 
    } 
    defer conn.Close() 
    
    // 发送数据 
    data := []byte("hello world") 
    err = conn.Write(data) 
    if err != nil { 
        log.Fatal(err) 
    } 
    
    // 接收数据 
    buf := make([]byte, len(data)) 
    err = conn.Read(buf) 
    if err != nil { 
        log.Fatal(err) 
    } 
    fmt.Println(string(buf)) 
}


在此示例中,我们使用ipoib.DialIB函数创建了一个RDMA连接,指定了本地和远程的IP地址和端口号,并设置了缓冲区大小为1024字节。然后,我们使用conn.Write函数向对方发送了一条消息,并使用conn.Read函数从对方接收了一条回复消息。最后,我们将接收到的数据转换为字符串并打印出来。


在实际应用中,需要根据具体的需求进行更复杂的RDMA通信实现,如RDMA读操作、多个连接的管理等。同时,还需要考虑RDMA通信的安全性和稳定性,避免出现数据丢失、死锁等问题。


四.NVME-oF是什么?

NVMe-oF(Non-Volatile Memory Express over Fabrics)是一种将NVMe协议扩展到网络存储中的技术。它可以让远程计算机通过网络访问本地存储设备,从而实现高性能、低延迟、高可用的存储访问。

NVMe-oF技术的实现需要硬件和软件的支持,包括网络适配器、存储控制器、驱动程序和协议栈等。在NVMe-oF架构中,存储设备被分为两个部分:NVMe-oF Target和NVMe-oF Initiator。

NVMe-oF Target是存储设备的服务端,负责响应NVMe-oF Initiator的请求,并将数据传输到本地存储设备中。NVMe-oF Initiator是存储设备的客户端,负责向NVMe-oF Target发送请求,并从本地存储设备中读取数据。

在NVMe-oF技术中,数据传输是通过RDMA(Remote Direct Memory Access)技术实现的,可以实现高带宽、低延迟的数据传输。同时,NVMe-oF技术还支持多种传输协议,包括RoCE(RDMA over Converged Ethernet)、iWARP(Internet Wide Area RDMA Protocol)等。

总的来说,NVMe-oF技术是一种将NVMe协议扩展到网络存储中的技术,可以实现高性能、低延迟、高可用的存储访问。如果你需要更详细的信息,可以查看相关的文档或参考其他相关资料。谢谢!


要在Go中实现NVMe-oF,可以使用GoNVMe库。GoNVMe库是一个基于Go语言实现的NVMe命令行工具和库,可以用于管理和测试NVMe设备和驱动程序。

使用GoNVMe库时,需要先安装NVMe设备和驱动程序,并安装GoNVMe库。然后,可以使用GoNVMe命令行工具来执行各种NVMe操作,如列出NVMe设备、读取NVMe设备的属性、执行NVMe命令等。


以下是一个使用GoNVMe库读取NVMe设备属性的示例程序:

package main 
import ( 
    "fmt" 
    "github.com/chenjie199234/GoNVMe/nvme" 
) 
func main() { 
    // 打开NVMe设备 
    dev, err := nvme.Open("/dev/nvme0") 
    if err != nil { 
        panic(err) 
    } 
    defer dev.Close() 
    
    // 读取NVMe设备属性 
    id, err := dev.Identify() 
    if err != nil { 
        panic(err)
    } 
    // 输出NVMe设备属性 
    fmt.Printf("%+v\\n", id) 
    }


在此示例中,我们使用nvme.Open函数打开了一个NVMe设备,并使用dev.Identify函数读取了NVMe设备的属性。然后,我们将读取到的属性信息转换为字符串并打印出来。

使用GoNVMe库需要有一定的计算机存储和系统编程基础,并且需要了解NVMe协议和相关的存储技术。同时,NVMe设备和驱动程序的支持也是使用GoNVMe库的前提条件。


图标赞 1
1人点赞
还没有人点赞,快来当第一个点赞的人吧!
图标打赏
0人打赏
还没有人打赏,快来当第一个打赏的人吧!