当前位置:首页 > CN2资讯 > 正文内容

穷人的分布式网络

3天前CN2资讯

前言: 穷人指没钱或者不愿花太多钱(我既属于前者也属于后者T_T),分布式网络主要指主机网络环境分布在不同的地理环境比如不同省或者不同国家(谁还没有一个比较便宜的国外vps不是~_~)

既然没有办法改变世界就改变自己吧.

需求

  • 将国内不同地区的云主机以及国外vps网络层打通,要求尽可能少的延迟以及高吞吐.
  • 注意:我是指尽可能的改善当前的网络环境,你当然可以说出一堆的极端环境,不过那真不是我想要解决的.

    方案调研


  • kcptun
  • 其他
  • 第一种解决方案可以解决主机之间的连通性,但是并没有试图改善网络环境不佳的局面,即,基于当前网络环境.再者各种***的安装部署都太负载且装一堆有的没的.

    其实我不太确定是不是所有ipsec或者其他***方式会不会通过一些机制解决网络环境不佳的情况,如有错误,还望指正.

    第二种方案中的kcp介绍如下

    KCP是一个快速可靠协议,能以比 TCP浪费10%-20%的带宽的代价,换取平均延迟降低 30%-40%,且最大延迟降低三倍的传输效果。纯算法实现,并不负责底层协议(如UDP)的收发,需要使用者自己定义下层数据包的发送方式,以 callback的方式提供给 KCP。 连时钟都需要外部传递进来,内部不会有任何一次系统调用。

    而kcptun则是在kcp协议的基础上,建立隧道,但只是映射某个端口,实在是让人难过,一个一个的端口映射很难维护.

    其他方案指代理或者其他***

    总结

    我暂时没有在找到比较好的解决方案,所以我打算自己撸一个,但是当下只是写了一个原型(可能没时间写),有兴趣且码代码速度快的参考一下我的设计开发一下,开发好了可以给我用用^_^

    设计方案

    编程语言

    golang

    google出品必属精品.

    传输协议

    kcp

    用于这里想解决的问题是网络环境不佳的问题,所以纯udp或者tcp都是不太适合的.

    功能组件

    • hubserver
    • peerclient

    业务逻辑

    hubserver

    hub启动流程
  • 监听端口
  • 维护peer列表
  • 心跳流程
  • 验证client
  • 判断ip及端口是否改变,是则更改peer列表,并推送给所有客户端
  • 如果client心跳超时,则该client从peer列表删除
  • 如果推送peer列表给某client失败,则将该client移除
  • peerclient

    peer启动流程
  • 向hub注册自己
  • 获取peer列表
  • 创建tun设备
  • 建立读写管道
  • peer读写流程
  • 解析数据包头部信息,获取目标地址
  • 查询目标地址是否在peer列表,是则发送,负责不响应
  • 心跳流程
  • 每秒向hub发送心跳,失败后尝试三次,依次为1,3,7秒延迟,尝试失败则不在响应tun设备,日志记录失败
  • 原型

    本来我的愿景要再大一些的,即不只是在有公网IP的境况下打通网络,且能穿透nat(指一定几率,有一些nat环境的确打不穿.)

    由于大部分时间是在研究怎么通过kcp在绑定特定端口的情况下既能充当client发起请求又能作为server接受请求,但是kcp虽然是基于udp,但是把自己包装成了类tcp,所以监听的境况下就不能使用类似WriteToUDP的方法发送了(当下的确没看到类似接口),所以如果使用kcp的方式穿透nat,那么心跳的流程以及存活状态的逻辑就变得比较复杂了,上面的设计方案主要指都拥有公网IP或者两端至少一段拥有公网IP的情况.

    所以原型中并没有设计kcp的代码.

    hubserver

    // fast***_hub.go package main import ( "log" "net" "os" "syscall" "time" // reuse "/kavu/go_reuseport" kcp "/xtaci/kcp-go" ) func main() { var err error var conn *net.UDPConn if len(os.Args) < 2 { os.Exit(-1) } raddr := os.Args[1] localAddr, _ := net.ResolveUDPAddr("udp", ":8887") serverAddr, _ := net.ResolveUDPAddr("udp", raddr) conn, err = net.DialUDP("udp", laddr, raddr) conn.W if err != nil { log.Println("dial udp err") log.Println(err) } _, err = conn.Write([]byte("hello")) if err != nil { log.Println("udp write error") log.Println(err) } buf := make([]byte, 1024) n, _, err := conn.ReadFromUDP(buf) log.Println("udp read data: ", string(buf[:n])) conn.Close() heartbeat(localAddr, serverAddr) klisten, err := kcp.Listen(":8887") if err != nil { log.Println("listen error") log.Println(err) } log.Println("accept start") for { conn, err := klisten.Accept() if err != nil { log.Println("accpet error") log.Println(err) continue } go func() { for { buf := make([]byte, 1024) n, err := conn.Read(buf) if err != nil { log.Println("read error.") log.Println(err) } log.Println("recieve data: ", string(buf[:n])) _, err = conn.Write([]byte("hello")) if err != nil { log.Println("kcp write error") log.Println(err) } log.Println("remote addr: ", conn.RemoteAddr()) time.Sleep(3 * time.Second) } }() } }

    peerclient.go

    package main import ( "encoding/json" "flag" "fmt" "log" "net" "os" "os/exec" "strings" // "fast***/common" "/songgao/water" "/x/net/ipv4" ) const ( BUFFERSIZE = 1500 MTU = "1300" ) var ( hubServer = flag.String("hub", "", "server addr like 192.168.11.100:8796") local = flag.String("local", "", "local ip like 172.16.97.101") listen = flag.String("listen", ":6222", "udp for bind") port = flag.String("port", "9999", "local port like 9999, default 9999, if you want to run multi clinet in same machine,change the port") peerMap = make(map[string]string) ) func checkFatalErr(err error, msg string) { if err != nil { log.Println(msg) log.Fatal(err) } } func runIP(args ...string) { cmd := exec.Command("/sbin/ip", args...) cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout err := cmd.Run() if err != nil { log.Fatal("Error runing /sbin/ip:", err) } } func main() { flag.Parse() // parse args if *hubServer == "" { flag.Usage() log.Fatal("\nhub Server is not specified") } if *local == "" { flag.Usage() log.Fatal("\nlocal ip is not specified") } if *port == "" { *port = fmt.Sprintf(":%s", *port) } // 将地址字符串解析成*net.UDPAddr hubAddr, err := net.ResolveUDPAddr("udp", *hubServer) checkFatalErr(err, "Unable to resolve server UDP socket") listenAddr, err := net.ResolveUDPAddr("udp", *listen) checkFatalErr(err, "Unable to resolve local UDP socket") // 初始化创建的tun设备的配置文件 config := water.Config{ DeviceType: water.TUN, } // 创建一个tun设备 iface, err := water.New(config) checkFatalErr(err, "Unable to allocate TUN interface: ") log.Println("Interface allocated: ", ()) // 设置ip地址并启动设备 runIP("link", "set", "dev", (), "mtu", MTU) runIP("addr", "add", *local, "dev", ()) runIP("link", "set", "dev", (), "up") // 监听一个udp socket,通过listenUDP创建的socket,既能发送到指定的ip地址也能接受 conn, err := net.ListenUDP("udp", listenAddr) checkFatalErr(err, "Unable to connect server") defer conn.Close() privateIP := strings.Split(*local, "/") // 将自己的内网IP发送给hubserver conn.WriteToUDP([]byte(privateIP[0]), hubAddr) go func() { buf := make([]byte, BUFFERSIZE) // 不停的接受信息 for { n, addr, err := conn.ReadFromUDP(buf) if addr.String() == hubAddr.String() { log.Println("recieve data from server:") // 解析hubserver发送过来的peermap err = json.Unmarshal(buf[:n], &peerMap) if err != nil { log.Println("peermap unmarshal error") log.Println(err) } } else { log.Println("recive data from peer:") } if err != nil || n == 0 { fmt.Println("Error: ", err) continue } log.Println(string(buf[:n])) // 将对端发送过来的数据写到本地的tun设备 iface.Write(buf[:n]) } }() packet := make([]byte, BUFFERSIZE) // 不停的读取本地tun设备接受到的数据包 for { plen, err := iface.Read(packet) if err != nil { break } // 解析数据包头部 header, _ := ipv4.ParseHeader(packet[:plen]) dstIP := header.Dst.String() // 如果数据发送的目标地址在peermap内则通过udp发送到对端 realDest, ok := peerMap[dstIP] if ok { realDestAddr, err := net.ResolveUDPAddr("udp", realDest) if err != nil { log.Println("resolve real dest ip error") log.Println(err) continue } fmt.Printf("Sending to remote: %+v (%=v)\n", header, err) conn.WriteTo(packet[:plen], realDestAddr) } else { continue } } }

    依次启动hubserver,peer1,peer2, 效果如下

    试试网络是否是通的.

    总结

    这个原型没有验证,也没有kcp,也没有数据加密...仅是最基础的功能被实现了,数据传递.

    源代码:https:///youerning/blog/tree/master/fast***

    主要灵感来自这篇文章:https://nsl.cz/using-tun-tap-in-go-or-how-to-write-***/

    但是我做了一下修改,将数据包转发到指定的对端.加了个hubserver.

    参考连接:

    https:///skywind3000/kcp

    http://colobu.com/2014/12/02/go-socket-programming-UDP/

    http://www.cnblogs.com/GO-NO-1/p/7241556.html

      你可能想看:

      扫描二维码推送至手机访问。

      版权声明:本文由皇冠云发布,如需转载请注明出处。

      本文链接:https://www.idchg.com/info/19080.html

      分享给朋友:

      “穷人的分布式网络” 的相关文章

      国内VPS全解析:选择最佳虚拟专用服务器的指南

      国内VPS的概述 VPS,或者说虚拟专用服务器,是一种将一台物理服务器分割成多个虚拟服务器,以便多个用户可以共同使用。这样的设定不仅能够充分利用服务器的资源,还为用户提供了更高的灵活性与控制权。对于希望在网上进行业务拓展或个人项目的朋友们来说,国内VPS是一个非常合适的选择。 国内VPS的市场发展迅...

      甲骨文云注册:详细流程与免费试用攻略

      甲骨文云注册概述 甲骨文云介绍和服务特点 我对甲骨文云的首要印象是它独一无二的服务。甲骨文云不仅提供高性能的VPS服务器,还给予用户一个轻松的起步体验。其主要服务包括两台配置为1核1G内存、50G硬盘和10T流量的AMD VPS,还有一台配置为4核24G内存、100G硬盘、10T流量的ARM VPS...

      如何高效使用测速脚本监测网络性能

      在互联网的快速发展中,网络测速变得越来越重要。作为一个互联网用户,了解自己的网络性能是否稳定,以及在不同时间与地点的表现,能帮助我们更好地选择服务和进行问题排查。网络速度直接影响了我们的在线体验,无论是看视频、玩游戏,还是进行远程办公,网络性能都扮演着至关重要的角色。 测速脚本出现在这样的背景下,它...

      SSH Client Windows 登录指南:轻松配置与高级功能使用

      SSH 客户端在 Windows 中的概述 SSH,也就是安全外壳协议,是一种用来在网络中进行安全数据传输的协议。它确保数据的机密性和完整性,这对于网络管理员和开发者来说是至关重要的。在Windows中,SSH客户端直接关系到我们如何安全地登录到远程计算机。通过SSH,用户可以安全地执行命令、传输文...

      选择野草云主机服务,享受高性价比与优质体验

      野草云是一家在2016年成立的主机服务提供商,由国人运营,专注于为中国大陆地区的用户提供优质的服务和产品。作为一家相对年轻的主机商,野草云力求用更贴近用户的方式来满足客户需求,特别是在国内市场需求快速增长的背景下,它的出现让很多用户找到了合适的主机选择。 说到野草云的历史背景,首先让我想起它在竞争激...

      Nginx Cache Control: 如何使用 No Cache 精确管理缓存策略

      作为一名开发者,我一直非常欣赏 Nginx 作为高性能 HTTP 和反向代理服务器的能力。Nginx 不仅在稳定性和可扩展性方面表现出色,它的缓存控制功能也相当强大。通过设置响应头,Nginx 能有效地管理客户端和代理服务器的缓存行为,让我在开发和部署时能够更灵活地处理资源的缓存。 使用缓存控制的好...