量变引发质变。html
因为咱们如今开发的云平台项目是一个跨云调度的重型计算平台,因此会用到不一样的云服务厂商的计算实例服务器,好比阿里云的ECS、亚马逊的EC2或者谷歌云的compute engine等,同时也会在这些计算实例之间进行数据传输。 这些服务器之间的传输速度一般是不一样的,即便是同一个云服务厂商内的不一样区域服务器之间传输数据,带宽也会有所不一样。 因此须要对这些服务器之间的带宽速度进行测量,以供调度进程分配任务和传输数据。 总结起来这个功能实现起来有3个要求:前端
针对这3个问题,我实行了如下解决方案。git
一种简单的设想就是启动测速所需的客户端和服务端,让这些程序之间互相争抢进行测速。 github
可是这种方式在进程的管理上很容易出现问题,由于每一个进程既要操做本身的状态还须要操做其它进程的状态,同时一会儿起4个进程也比较浪费,由于事实上每次测速只会有一个进程工做。 因此更好的方式是用主从结构,由一个主进程来启停负责测速的客户/服务端子进程。后端
这样不但能有效地避免资源的浪费和争抢,同时主进程中也能够集成不少逻辑功能,好比对外提供 REST API,记录日志、备份测试结果等。服务器
固然为了方便部署和程序控制,全部的进程都是部署在 Docker 容器中。网络
因为主进程须要访问宿主机的 Docker 服务,因此须要开启 Docker 的 remote API 服务,对容器提供REST API进行操做。前端工程师
网络协议是一层一层封装起来的,而TCP和UDP属于同一层的两种协议。 其中TCP协议在先后端开发中很是经常使用,由于REST API请求依赖的HTTP(S)协议就是TCP的上层协议,而UDP协议在视频、游戏、下载等业务中使用也很是多。 它们有一些共同点:请求的发起方称为客户端,请求的接收方称为服务端,服务端和客户端能够双向通讯。 而它们的侧重点有所区别。TCP更注重稳定,客户端和服务端之间须要创建链接以后才能互相发送数据。 UDP则更注重速度,客户端不须要和服务端创建链接便可直接发送数据,可是若是发送速度太快或者网络不稳定可能会形成丢包,致使对方接收的数据部分丢失。并发
经常使用的命令行测速工具备iperf和speedtest,相较之下选择了功能更强大的iperf。 iperf是一个比较理想的测速工具,支持TCP、UDP协议,还能够经过参数来制定传输数据大小、传输次数或者传输时间,以及输出结果的格式。 可是因为前面UDP协议的特性,测速会略微麻烦一些,须要找到合适的带宽。 好比按照1Gbps的速度发送数据,丢包率是70%和按照10Mbps的速度发送数据,丢包率是0,那么对数据完整性有要求的话确定更偏向于后者。 固然实际状况并非对于丢包率为0就是最好的,而是在可容忍的范围内采用最大速度传输(数据丢了还能够重传不是~)。 这就意味着须要根据实际网络情况不断调整和尝试。 而iperf并无这么智能,因此UDP这一块采用团队内部开发的一款UDP传输工具,来找到理想的传输速度。框架
要保证满带宽只须要保证测速时没有其它程序占用带宽便可。 因为咱们能够启动一台独立的抢占式服务器来运行测速程序,因此其它非测速程序的进程不太可能占用带宽,而容易争抢带宽的是用来测速的子程序。 因此须要让子程序之间是互斥运行,甚至是互斥存在的。 采用状态管理基本上就能够实现,主程序在每次有进程启动的时候将状态置为"connecting",测速完成后置为"waiting",只有在"waiting"状态下才能够启动新的子程序进行测速。 可是这只是从代码逻辑层面控制,对于稳定健壮的程序而言,最好还有其它的硬性控制方式。 这时候使用容器的话就能够轻松办到。 凡是须要进行测速的进程都在容器中启动,同时容器的名称都统一,那么一旦程序出现bug,同时启动多个子程序时,Docke r服务则会报错,告知容器名称冲突,从而建立失败。 固然这种方式也有必定的风险,好比上一个进程测速过程当中出现问题没有按时退出,那么则没法进行新的测速,因此须要须要设定一个超时时间,超过一段时间后主动中止当前测速子程序。 同时若是主程序意外退出,致使中止失败的话,也要进行处理:在每次启动主程序的时候进行检查,及时销毁未中止的子程序。
多节点算是很是棘手的问题。试想若是在一段时间内同时在多个云服务器上启动多个测速程序,如何保证他们有序的进行测速呢? 要解决这个问题,先思考一个简单些的问题: 在一段时间内,如何决定哪些云服务器启动服务端子程序哪些云服务器启动客户端子程序呢? 若是按照“主-从”模式的话须要创建一个中心节点来进行控制,可是这样的缺点不少,最重要的一个缺点是若是某个节点与中心节点没法通讯那么就没法得到与其它节点通讯的机会,及时它和其它节点之间网络畅通。 同时中心节点和其它节点之间也存在多节点通讯的问题。 总而言之这种方式下通讯的成本过高,服务端与客户端传输数据须要的中间环节太多,很容易出现问题。 因此简单的方式是让云服务器之间互相发起测速请求并响应。 这样的话,主程序的逻辑要分为两个模块,一个模块用来响应请求、、分配端口、启动服务端容器。 另外一个用来轮询带测速队列并发起请求、启动客户端容器创建链接。
工做流程大体以下:
这种处理方式还有一种极端状况,就是两个云服务器之间互相请求进行测试,若是双方请求到达时间一致,那么就会同时给对方分配端口,而后同时受到对方分配的端口以后发现服务端已启动因而放弃链接。 因而出现了相似进程“死锁”的状态。 对于这种状况的处理方式是使用时间戳来记录请求发起的时刻,双方经过时间戳的前后来决定是否启动客户端或服务端。 即便更极端的状况出现——双方时间戳相同。那么经过超时回收或者发消息释放端口来创建下一次链接。
弱网络指的是网络不稳定或者带宽较小的状况。 这种状况的处理方式原则上就是重测,可是关于重测有几个须要注意的地方:
不少时候实现一个功能并不困难,可是要把功能实现好倒是一件不简单的事。 虽然理论上实现起来只是简单的调用测速工具就能够获得结果,但在实际场景下可用性会变得很低。 好比没有对弱网络的重测机制,那么偶然的网络抖动就会影响到测速结果。 若是没有考虑到多节点争抢链接的问题,那么实际运行在多个云服务器上可能会形成程序错误或测速结果不许确的问题。 要怎么样把功能实现好呢? 至少有两个考虑方向:
原文连接:tech.gtxlab.com/test-speed.…
做者信息:朱德龙,人和将来高级前端工程师。