相关文章:

notr内网穿透发展历程

我们开发了一个内网穿透软件Notr,整个软件经历了将近一年的变更,最近软件基本稳定,用户基本也没在提bug了,很是欣慰。也是由于最近处于待业状态,有的是时间,所以将整个软件的版本变更做一次记录。

项目起源

Notr项目的起源是我自己业余时间写的一个开源的项目gtun,gtun本质是一个微PN,写gtun最主要的目的是解决两个问题:

  1. 方便在家里能够访问公司
  2. 家里的上外网用

目前家里一直都有在用gtun来上外网,主要是结合树莓派做网关,所以基本上家里所有设备都能够使用。

至于访问公司内网,这个是个相对比较危险的行为,我们小公司管理得比较松散所以要用的时候偶尔还是会用的。

gtun项目写完一个版本之后,我才了解到内网穿透这个词,但是gtun做内网穿透做得还不够彻底,我思考了下,认为在gtun项目当中加入完善的内网穿透功能,只会让gtun忘记了它的使命,会变得非常臃肿,于是我将这个功能独立出来写了一个专门解决内网穿透的项目——Notr

对Notr的最初定位主要有两点:

  • Notr本身提供的是内网穿透的服务,所以是一款产品
  • Notr软件本身应该尽可能保持软件的简洁,不用加太多花里胡哨的东西,它最基本的使命就是让用户知道需要穿透本机哪个端口就能够使用。

立足于以上两点,我们在gtun内网穿透功能的基础之上,加入了动态域名解析(DDNS)以及服务注册等功能。最终呈现给用户的结果如下所示:

使用截图

Notr技术变更

谈完了项目起源,接下来应该要谈谈项目具体的技术变更过程,一步一步看整个项目经历了哪些调整。

初始版本

初始版本很简陋,就两个程序,也是整个系统最小的一个模型,包括客户端和服务端两个,服务端有两个功能:

  1. 服务客户端(验证,session保存,IP地址分配等)
  2. 为每一个需要穿透的端口启动一个本地端口与其对应,作为中转

v1.1

这一版本主要问题:

  1. 首先是需要知道server的IP,端口等信息,我们总不能告诉用户指定哪台服务器吧
  2. server的端口信息不固定,特别是针对http和https,公众号调试非80和443端口用不了
  3. 用户的验证的key是在server节点手动指定的
  4. 用户最后拿到的是映射到公网的IP地址

总而言之,这一版本原因谈不上产品,纯粹是一个Demo,自己用完全没问题,但是作为服务给别人用,显然就拿不出手。

添加服务注册节点的版本

为了解决上一个版本的第一个问题,也就是IP地址和端口需要客户端指定的问题,我们决定开发一个接口(这一接口所在的模块我们称之为registry或者controller),让客户端在服务端之前,先去调用这一接口,拿到server的IP和端口信息,也就是说这一接口服务具备一个全局的视野,保存所有server节点的信息,这里面需要考虑一个问题——registry如何知道server节点的存在以及server节点是否还在工作。

最初的解决方案是启动的时候通过http调用registry的一个register接口,退出的时候再调用registry的unregister接口,但是有时并不是说没调unregister就说明服务在运行,于是将server和registry的通信协议从http协议换成了tcp长链接。

为了解决第二个问题,也就是针对http和https无法做到端口复用,多个用户无法共用80和443端口,我们开始引入了nginx,通过nginx帮我们实现http和https的代理。这个过程很容易理解,我们让nginx帮我们做反向代理了,我们不再自己监听端口,nginx根据Host来做区分。

为了解决第三个问题,我们开始往registry模块引入功能,这也是Notr从一个Demo转向产品的最主要的一个步骤。我们在registry模块里面加入了用户模块。所有用户的注册登录以及key管理,都在registry模块实现。

为了解决第四个问题,我们添加了一个与域名相关的模块——DDNS,我们DDNS的原则是:

  • 如果用户已经注册,那么我们根据用户名信息生成一个固定的域名
  • 如果用户没有注册,我们生成一个随机域名,让非注册用户,也能体验

这样,无论底层server节点如何切换,对于用户而言都是透明的,用户感知不到,只是我们后台做了域名解析记录的修改而已。

这一版本的时序图如下所示:

v1.2

到目前为止,这一版本基本可以开始拿去给别人试用了,此时软件问题开始慢慢暴露:

  • 本地的服务需要监听0.0.0.0,因为我们真实访问的是本地虚拟网卡的IP地址,而很多web容器可能默认监听的是127.0.0.1
  • windows需要安装tap驱动,劝退了不少人
  • 需要管理员权限启动,很多用户安全意识很高,又劝退了一部分。

这个问题持续了很久,最后在春节放假期间进行了一次方案调整,把这些问题都彻底消灭掉了。

不使用虚拟网卡的版本

在上一个版本中,所有的问题都出在方案使用了虚拟网卡构建虚拟局域网上,正是因为使用虚拟网卡,所以windows需要安装tap驱动,也正是因为需要创建虚拟网卡,所以才需要管理员权限启动程序。

最后我们决定把虚拟网卡去掉,但是对外而言,几乎没有发生任何改变,我们虽然消除了虚拟网卡,但是内部从server到client,依然是可以通过虚拟IP地址进行通信的,所以就Nginx反向代理这一层,完全不需要做任何更改,客户端相对上一版本而言,减少了虚拟网卡的安装以及管理员权限,于用户而言应该是更加方便了。

从技术细节上讨论这个版本的改变,我们关键的更改在于用DNAT将Nginx的反向代理流量全部导到我们server监听的一个tcp端口,并在内部找到这一数据流对应的客户端,将数据转发到客户端上。上一版本完全是通过路由的方式进行,这是这两个方案最大的区别。

负载均衡策略(2019.06.11)

目前notr内网穿透服务节点均部署在中国大陆境内,最近碰到一个海外用户问题,有个海外用户希望使用,客户端运行在新加坡,根据旧的负载均衡策略,会根据每个服务节点服务的客户端数量来选择节点给客户端。这里就会有个问题:

  • 海外客户端跟中国大陆通信,长距离tcp丢包严重,延时高

根据用户反馈,延时达到两百多毫秒,使用ssh时有卡顿现象,不是很顺畅,当时紧急开启了一个香港节点,但是根据原来的负载均衡策略,用户只能说有一定概率会走香港节点,并不能保证一定走。为了解决这个问题,Notr的负载均衡策略添加一个机制:

  • 根据客户端IP来判断客户端所属区域,然后选择该区域的服务节点

这样保证了海外用户连海外的节点,延时从245ms变成43ms。使用也更加顺畅。

这个功能很早之前就想过添加,但是鉴于当时用户基本都在国内,国外用户基本没有,基本用户也能接受,没有想到会有海外用户想要使用,所以就一直没有开发这一功能,目前该功能已经正式上线。

最后

Notr目前还有很多可以看见的问题需要解决,但是个人精力有限,进度相对会比较慢。

前段时间有个用户截了他的用户信息给我,我看了下,是2018年8月份过期的,当时每个用户的试用期是一个月,也就是2017年7月份开始的第一个用户,当时就很感慨,Notr已经写了这么久了吗?同时也感到高兴,很高兴自己能够坚持做这件事做这么久,虽然不知道结果会如何。