藏川线前段

--- 摄于 2017 年 9 月 藏川线前段

从网络发现开始讲起

最近在 Tentacle 库里面加了个特性,并且 ckb 升级依赖时,在部分机器(Linux only)上开启了这个功能,虽然代码不是很多,但实际上涉及的底层知识很多并且很杂,需要翻阅很多资料才能理清内部的脉络,这篇,算是一个汇总归纳。

请注意,我并不保证所说的一切都是对的,请自行甄别

网络发现

从 internet 诞生以来,网络发现就根植于 IP 协议之中。日常中,无论使用什么网络服务,基本上无法绕开 DNS 的域名查询服务,这算是一种网络发现方式。那么,我们为什么需要网络发现这样的服务呢,原因就在于,任何计算机都无法自行了解在网络上的特定服务在什么 IP 地址上,这相当于,送信不知道门牌号,这样是无法工作的,于是有了 DNS 这样的域名服务,通过域名查询找到对应服务的 IP 地址,然后通过 IP 协议,将上层的数据包发送到网络(硬件)上。

整个过程是:用户请求服务 -> 本机查询找不到 -> DNS 查询 -> 得到响应后组装 IP 包发送 -> 等待响应 -> 交给用户

TCP/IP 协议

A TCP/IP Tutorial:https://tools.ietf.org/rfc/rfc1180.txt

说到 IP 协议,我们不得不引入经典的 TCP/IP 协议族,它泛指当今网络上的各种协议及基于其之上的各种应用,更准确的说,它应该叫做 internet technology

下面是最简单的单机网络逻辑结构:

                     ----------------------------
                     |    network applications  |
                     |                          |
                     |...  \ | /  ..  \ | /  ...|
                     |     -----      -----     |
                     |     |TCP|      |UDP|     |
                     |     -----      -----     |
                     |         \      /         |
                     |         --------         |
                     |         |  IP  |         |
                     |  -----  -*------         |
                     |  |ARP|   |               |
                     |  -----   |               |
                     |      \   |               |
                     |      ------              |
                     |      |ENET|              |
                     |      [email protected]              |
                     ----------|-----------------
                               |
         ----------------------o---------
             Ethernet Cable

                  Figure 1.  Basic TCP/IP Network Node

任何消息发送,都需要通过这样的一套自上而下的流程,将 IP 包通过硬件接口转换成电信号,发送到 Ethernet 上去,发送之前,通过各个设备的 ARP 进行查询路由操作,用来填充 IP 包头上的目的地址。

在同一个网络内的设备互联就叫直接路由,不同网络之间的设备互联就叫间接路由,而多个网络之间的连接点,这是一台转发设备,称之为网关/路由器,多网络之间的交互操作,都需要经过网关进行转发。

由于公网 IPv4 分配枯竭的问题,IPv6 的诞生和替换是必然的趋势,但是,在替换期间内,必须要有一种技术能够将公网地址共享给内网设备,使其能够对外进行服务,这就是所谓的 NAT(Network Address Translator),甚至在 IPv4 和 IPv6 之间的相互通信当中使用 NAT-PT,也是一种地址转换。

NAT 的出现,将网络架构推向了愈加复杂的地步,往往自身所在的网络实际上是多层 NAT 设备之后的一个极小局域网之内,带来的问题是,如何才能在自身所在的网络下,启动一个服务,能让任何用户进行访问呢?这也是 p2p 网络拓扑下的最大问题。

愈加复杂的网络应用

现在,网络服务规模越来越庞大,对应产生的数据也越来越多,单机解决问题的成本上升到无法支持后,分布式服务的架构水到渠成的进入各大服务商的眼中,无论是数据库服务,还是其他的应用,开始走上数据分片管理,操作共识执行的这条路。这时候,如何将分布式服务组成一个自有网络就成了一个问题,于是,像 service mesh 等类似的服务就出现了,将属于一个服务的各个微服务组成一个自有网络,屏蔽掉网络发现的过程,只需要知道结果就可以了。

说句题外话,我个人认为,云服务的架构和 p2p 的架构目前各自完好地运行在同一个网络上,但它两完全是背道而驰的理念,各自基于不同的假设,进行不同的优化。云服务上,很大篇幅在讨论如何让流量负载均衡,如何高可用,如何用可伸缩抵御突然膨胀的高流入,譬如 k8s 集群下的方案,而 p2p 应用上,讨论的是,如何让整体网络更加健壮,如何让更多的点更快更好地加入网络。

发现方案

服务完全处于公网环境,并数量可控

我们看各种集群的服务发现,可以看到很多的方案是用 etcd 数据库做服务发现管理,任意服务的加入和退出,对应的反应是 etcd 中数据的增删,每个服务都可以通过位于本节点的配置来实时看到共识的结果,这是一种非常方便的服务发现方式。但这适用于真的完全云服务,也就是服务之间可以很轻松地通过公网进行交互。

当服务在同一局域网及其子网,并数量不可控

mdns 组播方案是一个最好的选择,它将消息通过组播的方式发出,对于感兴趣的服务,可以自行接收并响应互联。

当服务处于 NAT 设备后的私有网络时

使用类似 UPNP 协议可以通过注册路由与本机的映射关系,将设备的服务端口与路由的端口进行绑定转发,这样,只要用户处于一层 NAT 设备后方,就可以非常轻松地将服务暴露到公网,但是,对多层 NAT 后的设备,无法操作。

在多层 NAT 设备之后的服务,这是最麻烦的一种情况,同时,它也是 p2p 网络拓扑下最常见的场景,无法确定使用方的网络情况,作为个人用户,常见的操作就是在个人电脑上启动应用,而这,大概率就是 N 层 NAT 设备后的网络。

  1. 公网 Proxy

这个方案的前提是,用户拥有一台服务器,服务器上启动代理服务,并与私有网络的机器互联,将对应的服务端口映射到服务器的任意端口,然后公网用户就可以通过该端口,使用私有网络的对应服务。看上去很完美,但,这种方案有决定性的缺陷:不是任何用户都愿意维护使用一台公网服务器的,这意味着额外的成本,包括时间和金钱。

  1. 利用网络本身开启 IP 隧道

使用到的原理类似于 STUN: https://tools.ietf.org/html/rfc5389

当私有网络的应用与公网的某一台服务器保持连接的时候,这意味着,在 IP 协议层,必然有一条已经存在于网络之上的通路,无论它是怎么形成的,它必然是通路。这时候,如果将访问的 IP 包从公网观测到的 IP 写入,那么通过这条已经存在的隧道,就可以将 IP 包顺利发送到私有网络的 IP 地址对应的机器上,之后剩下的就是如何让机器上的服务能够处理该 IP 包的问题。

基于 TCP 的 IP 隧道

reference:http://bford.info/pub/net/p2pnat/

reference:https://stackoverflow.com/questions/14388706/how-do-so-reuseaddr-and-so-reuseport-differ

首先,需要理解, IP 协议负责将数据通过路由转发到对应的目的地址,TCP 负责维护数据的完整性/可靠性/连接管理/重发控制/窗口控制等等。TCP + IP 共同确认 源地址、源端口、目的地址、目的端口。仅 IP 包发往对应地址,而没有对应端口进行接收处理就相当于什么都没有做,该包将会被丢弃。

其次,私有网络的服务对公暴露,对于 TCP 服务来说,就是让监听端口能接到公网的 TCP 报文,报文中带有 SYNC 标识,这就是请求建立连接的信号。

对于私有网络的服务来说,建立 TCP 流到公网服务器,就是自己作为 Client 连上一台公网服务器,但,可惜的是,此时,对应的源端口是错的,默认情况下,发起连接时,将分配一个随机端口,这样的 IP 隧道哪怕建立成功,也无法服务。这时候需要开启一个叫 reuse address 的特性,将源端口改为服务监听的端口,这样,随机端口就会被修改为响应 SYNC 包的监听端口,从而顺利启用使用 IP 隧道。

当然,不要天真地以为这就好了,这是有很大概率会失败的。。。。如果 NAT 设备搞事,那么,你的 IP 隧道就相当于不存在,然后一切都完了(这在国内是正常预期 : )

小结

一句话说明就是,ckb 在 linux 下默认开了 reuse address/port feature,与此同时,discovery 协议对应进行了可广播地址的更新,内核版本小于 3.9 的用户,应该会没法用了,同时,我期待大规模上线之后,是否会提高网络的健壮性,内网用户一直游离在网络的边缘地区一直是一个很讨厌的问题,期待能够解决。

评论区

加载更多

登录后评论