大文件下载的一些经验
Tag 下载, cdn, on by view 45

数年之前,我在一家CDN公司上班。在CDN项目主要工作是负责对客户视频数据搬运到云存储,中间也涉及到下载转码上传之类的工作。其中踩过一些坑,也吸取了一些经验。下面简单讲一下我遇到的几个问题。

下载没网速了

首先是下载没网速的情况。在处理大量的下载任务过程中,发现了一些特殊情况,客户通知我的服务回源,我的下载服务从源站下载一些大文件的时候,开始网络一切正常,可是逐渐的网速越来越低,甚至没网速,这回导致下载一直卡主。可能你会说,设置一个下载超时不就好了,但是,由于客户的文件大多是影视原片,体积巨大,有些甚至达到了几十G,如果简单的设置超时,以处理时间判断下载成败,那么这些大文件基本上不可能成功,都会以超时失败。当然,也可以分片下载来解决文件大的问题,但是对于0网速或者只有几B/s的网速,你的分片就显得无力了。其实,这个状况产生的原因大多是因为源站故障导致的,比如负载高之类的。只要断开连接重新下载就有机会恢复正常速度的连接,而且一般源站是有多个的,重新连接到新的节点就好了。所以,这里“断开连接重新发起下载”这个能力就非常重要了。我原本使用的是github.com/nareix/curl这个库进行文件下载,它的优点是创建异步下载任务后,可以监控当前下载的速度。这样,我可以对每个下载任务的网速进行监控,当网速低于一个阈值的时候,就放弃下载并重试。最初的时候只是简单的调用它的Close()方法,关闭了下载。后面却发现,经常发生fd用完的情况,一查才知道Close()方法并不能释放连接。最后只好自己修改了这个库,给它添加了强制关闭连接的能力。于是就完美的解决了这一问题。记得当时有一次,源站故障了,客户反馈说,它们接入的多家CDN厂商都上报,文件下载失败了,只有我们公司文件下载成功了,听到这消息,我嘴角不由得上翘,漏出了得意的笑容。

并行写同文件

遇到另一个印象深刻的问题就是某客户反馈文件错误的问题。客户反馈说他们在我们CDN上的文件播到一半卡死了,查了文件之后,发现文件大小正常,但是文件只有前半截有数据,后半截全是0,空的。当时,老板CTO领导全站在我工位后面,讨论这个问题,我也是第一时间加回了MD5校验,因为之前在我同事的要求下(同事是老员工、他负责任务调度、我负责几个agent,相当于他为主),去掉了上传前MD5校验,以节省任务时间。当时非常紧张,但是一时间也查不出原因,只能先加MD5校验,然后开发专用校验程序,将半年前开始的所有上传文件重新拉下来做MD5校验,查出异常文件重新上传。后来在一个偶然的机会下,发现了故障的原因,居然是调度测下发任务之后,等待N分钟之后agent还没上报成功,于是又重新下发了这个任务,然后刚好是同一个节点的agent收到该任务,这样一来,同一个节点上2个相同的任务在处理,文件路径一致,也就是说,有2个线程在从源站拉文件写到本地磁盘的同一个路径,一先一后,先处理完的线程开始上传了,后处理的还在写文件,后处理的写文件之后就会将该文件后半截变为空(这里与文件打开模式有关),这样前一个线程在上传的时候,文件其实变成半截了,再加上没有MD5校验,所以,故障就此发生了。

所以之后我就给每个任务的本地路径前加了一个uuid目录,这样一来,不管任务是否同一个文件路径,都不会相互干扰。每个任务都有自己的独立路径了。原程序是我接手别人的,虽说这问题不是我引入,但是,平心而论换做当时的我,也是想不到这一点的。不过,吃一堑长一智,现在只要涉及到本地资源存储,我基本上都会给路径前添加uuid前缀。


最近关于家庭网络我做了这些事
Tag 网络, on by view 67

软路由

我使用软路由已经有几年了,从最早的4网口工控小主机,到现在的r4s,一直是在用openwrt软路由作为家里的主路由。具体为啥要用软路由,相信懂的都懂。

vfxfwh7n

r4s相较于我之前用过的4网卡工控小主机,只有2个千兆网口,要小很多,很容易放进弱电箱。另外,由于我原来的工控机是英特尔J1900处理器,相较于使用arm处理器的r4s,r4s要节能得多,而且发热量也更小。

无线网络

无线网络,从最初的单路由器,到家里部署两个路由器,再到现在基于ap+ac部署的分布式无线网络。最初在老家县城的时候,由于WiFi普及率不高,基本无信号干扰,所以一个最基础的普通WiFi路由器就可以覆盖整个房子,网速也还行;后来老家农村重新建房了,3层楼房,用了2个路由器设置了两个接入点,但是从二楼到一楼切换的时候会有较长时间中断,或者是没有能切换,连接的是信号较差的二楼接入点;这种情况比较影响网络连接。所以,上次回农村老家后,将网络改为了ac+ap的部署模式,设备可以在ap间漫游,几乎不会中断网络。顺便整理了一下弱电箱,理线过程中发现光猫经常发生光信号中断的情况,最后发现是盘纤问题,光纤的尾纤弯折程度太大就会导致光信号中断,于是用绑线带重新绑了一下光纤,以免出现折纤的情况。

rdz4f7a1

同时,这一波处理也将预埋的网线接入到路由设备,每个预埋网线的房间都有有线网络了。

展望未来

未来家庭万兆迟早会普及,但是按现在的标准,万兆网线要使用八类线,八类线最大的缺点就是太粗太硬,不便于穿管,而且成本不菲,并且万兆电口网卡目前也是非常昂贵。不过如果是光纤就不一样了,单模双芯光纤在某宝上就有售卖,非常便宜,而且万兆光口网卡相较于万兆电口网卡便宜很多,但是这里面有一个额外的成本,那就是光模块,如果是全新万兆单模双lc光模块,价格也不菲,不过呢,某鱼上的二手模块却是很便宜,24块钱一个,我直接卖了一盒10个,据说是从服务器上退役下来的,都是卖家测试过没问题发货,并且收到后,我自己测试过没问题。另一个设备就是光交换机,万兆光交换机的价格与万兆电口价格差别也很大。因此,全屋万兆方案我更看好全屋万兆光纤的方案。假如以后我有机会装修自己的房子的话,我肯定会要求装修师傅给我单独埋一根光纤管线,然后预埋光纤,进入万兆网络时代,这样我的nas也能发挥他的最大性能。


不更新你可能以为我已经挂了
Tag 琐碎, 博客, on by view 72

上一篇是2019年。为什么这么久没更新?两方面原因:第一是自从加入现在的公司后,近几年更加忙碌了,没那么多时间;第二是博客的发布后台被我改版了,没时间完善,所以发文章的功能还不够完善。

是的,我和我的博客都没挂。并且,最近比较流行暗黑模式,无论是app还是网站,但凡不支持暗黑模式,就显得特别碍眼,所以我就索性更新了一下样式,支持了暗黑模式;亮白主题与暗黑主题根据读者计算机系统上的主体模式自动变化。你系统是亮白主题,那么你浏览我博客页面也是亮白主题;你系统是暗黑主题,那么你浏览我博客页面也是暗黑主题。另外,还给博客添加了一个搜索功能,基于bleve的搜索。

这几年,除了疫情,其实也没什么特别的。和之前一样生活、上班,如果非得说有所不同,只能说无论是生活还是工作都比之前更忙了。


糖糖发烧了,好担心
Tag 琐碎, on by view 1034

糖糖昨天下午开始就发烧了,晚上喝了布洛芬退烧了,半夜2点又发烧了,爸爸妈妈外婆起来给糖糖吃退烧药和物理降温,今天一早又发烧了,温度高的时候有39度。一早妈妈请假,外婆和妈妈带糖糖去儿童医院看病,爸爸去公司上班。

爸爸现在完全无心工作,看病回来下午这会儿糖糖又发烧了。再加上公司的事情,昨天凌晨手欠在公司某系统评论了某事,一早被领导diss了,心情真是糟糟的,烦烦的,真是手欠,半夜两点还关心公司的事情,更欠的事情是还顺手评论了某事。

希望糖糖能够早日康复,健康快乐成长。公司的事情该忙的还是要继续忙,吃一堑长一智,谨行慎言。


矛盾
Tag 情感, on by view 1294

已经结婚快要2年了,娃也已经开始整天“babababa”的咿呀学语了。

休完产假刚上班,工作上的事情并不太多,挺闲的。所以就上网刷知乎玩儿。最近刷到几个问题,“爸爸能爱女儿到什么程度?”,大概就是这样的问题,主题在表现爸爸对女儿的爱。刷了挺多答案的,看到了各种各样爸爸疼女儿的回答。每个回答都是满满的爱,虽然都各有各的可爱,每个故事都可爱的与别人不同。

想到了娃爸,似乎没有网上那样,难道这就是说的别人家的爸爸?难道我糖以后也只能看着别人家的爸爸然后满满羡慕?每次想到这里都很替我糖抱不平,会找娃爸抱怨几句。

但是回头想一想,其实娃爸也很爱臭糖,从来都想要把最好的给她,从来都愿意惯着她的小脾气(别看小小一个人,还穿纸尿裤,甚至还不会爬,但是小脾气绝对是足足的)。比如臭糖要吃奶睡觉觉,比如臭糖要抱抱不肯自己做着玩,比如臭糖要人陪着不肯自己呆着,比如臭糖睡觉觉的时候要把腿放在我身上,比如臭糖吃奶睡觉觉的时候大脚丫子使劲在我身上蹬,等等等等这些时候。她爸都会说,哎呀你糖喜欢这样就让她这样嘛。嗯,反正折腾的都是麻麻,不是爸爸!手动白眼。

这么说来,似乎糖爸对糖的爱也很假,都不是需要他付出的他就很慷慨,是不是这样?老实说,心里有时候也是这样觉得的。可是看着糖爸的眼睛,又知道他其实是真的很爱糖。

其实糖爸很信任我,他只是将我当成了他最放心的人,将他心里最重要的部分交给了我,并认为我一定能照顾好他最爱的人。

所以,我想他是爱我的,很爱我。不然,如何敢将自己最脆弱的部分完全依靠在我身上。虽然有时候又会突然怀疑他是不是像别人口中的渣男一样,并没有我想的那么伟大,只是单纯的不在乎我和糖。不过静下心来,我还是选择相信我的男人。

即便心里偶尔很矛盾。偶尔不平衡。



对gin应用基于TCP窗口的流量控制
Tag gin, tcp, 窗口, 流量控制, on by view 3289

TCP可以基于滑动窗口进行流量控制,使用setsockopt系统调用实现,可以限定客户端或者服务端连接的入网或出网流量,http是基于TCP协议的,因此http也可以基于TCP滑动窗口实现流量控制。golang自有的net包不支持server端TCP窗口设置,因此无法直接实现基于TCP窗口的流量控制。今天我们要对一个基于gin实现的微服务进行流量限制。

首先,gin自带的r.Run()启动的http肯定是不行的,然后http包中的http.ListenAndServer()也是不行的,那么我们就基于TCP来实现,但是golang得net包中的net.Listen()也是不行的。这时候我们只有调用底层的系统调用了(不是cgo),我们可以使用syscall包来实现系统调用。我们分为五步:创建socket,设置socket选项,绑定端口地址,转换为golang的listener,listen。

  • 创建原生的socket

s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, 0)
if err != nil {
    log.Println("create socket failed, err:", err.Error())
    return
}
  • 设置socket选项

// set receive buffer here
err = syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_RCVBUF, 2350)
if err != nil {
    log.Println("set socket option receive buffer failed, err:", err.Error())
    return
}

// set send buffer here
err = syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_SNDBUF, 2450)
if err != nil {
    log.Println("set socket option send buffer failed, err:", err.Error())
    return
}
  • 绑定端口地址

err = syscall.Bind(s, &syscall.SockaddrInet4{Port: 8099, Addr: inetAddr("192.168.31.11")})
if err != nil {
    log.Println("bind socket failed, err:", err.Error())
    return
}
  • 转换为golang的listener

f := os.NewFile(uintptr(s), "")
ln, err := net.FileListener(f)
if err != nil {
    log.Println("create listener failed, err:", err.Error())
    return
}
  • listen

err = syscall.Listen(s, 0)
if err != nil {
    log.Println("listen failed, err:", err.Error())
    return
}

最后我们把我们自定义的支持限流的listener应用于gin上

r := gin.Default()

r.GET("/", func(context *gin.Context) {
    context.File("socket")
})

err = http.Serve(ln, r)
if err != nil {
    log.Println("create http server failed, err:", err.Error())
    return
}

一个支持限流的http server就此实现。


将caffemodel编译为movidius的graph模型
Tag caffe, model, movidius, on by view 3015

首先需要安装ncsdk, 去movidius官网下载 https://developer.movidius.com/start 

mkdir -p ~/workspace
cd ~/workspace
wget https://ncs-forum-uploads.s3.amazonaws.com/ncsdk/ncsdk-01_12_00_01-full/ncsdk-1.12.00.01.tar.gz
tar xvf ncsdk-1.12.00.01.tar.gz
cd ncsdk-1.12.00.01
make install

安装完毕后可以调用mvNCCompile进行编译

mvNCCompile ./deploy.prototxt -w ./resnet_50_1by2_nsfw.caffemodel -s 12 -0 ./nsfw.graph

若是遇到编译失败

mvNCCompile v02.00, Copyright @ Movidius Ltd 2016

Error importing caffe

请设定环境变量后重试

export PYTHONPATH=/opt/movidius/caffe/python:$PYTHONPATH

编译成功后会生成 msfw.graph 文件,接下来可以使用 movidius 和 graph 模型进行推导了。


幸福的孤独者
Tag 散文, on by view 2748

人,一旦进入天命之年后就意味着进入了孤独期。尤其是独生子女家庭。记得二十几年前,父亲常常喊:“兵伢,来,来坐哈?”那时无知的我面对老父的招呼,显出明显的不耐烦,甚至冷漠,无动于衷。那时我是多么的冷酷自私呀!为什么那时我心中就没有一丝的对老人的怜悯或同情呢?其实我内心是非常悲悯老父老母的,只是自己不愿表露而已。我不想因为自己的“行孝而招来弟兄之间更深的仇隙”。这就是事实的症结,别人的不孝姑且不论,人在做天在看!我心底的内疚从没有消失过,时至今天他还让自己难安。就让自已在时间中慢慢讦悔吧!那时,而且那时父亲已经身患重病:老血栓。父亲在喊叫几遍无应后,他就孤独地喃喃自语地拖着垂暮之年的病弱之躯慢慢地走入他那低矮的老房子……

当时我为什么心中有恁大的抵触情绪呢?根源还是因为弟兄之间的不和。这不和的根源多是由老人的厚此薄彼引起的,当时自己为什么就过不了自己心魔那道坎呢?归根结底还是自已太自私了。现在父亲走了十一年,母亲也走了三年,我们也进入了天命之年还多五的光景,幸运的是我们还在工作还能工作!虽然如此,空闲时就感觉日子中似乎少了点什么?那就是孩子不在身边的空虚!这时自已就常常于寂寞中感受到“孤独的可怕”!是的,父辈们的“今天就是我们的明天。”“我的今天就是父亲的昨天”啊!我这不孝之子,现在终于体味到当年父亲为什么老是喊我陪他坐坐……?啊!往事堪追忆?可是这后悔能改变一切吗?能回到当年吗?能让自己重新“行孝”吗?俱往矣!现在我终于懂得了父亲当年常对我唠叨的“养儿方知父母恩”是什么意思!“寂寞使人孤独,孤独使人易老”。我现在感受到“寂寞的可怕”,或者这是“上天对自己的警告吧”!我有个和协美满的家,可是一家几囗常年在外工作,儿子儿媳他们就是把全年的假期算在内,在家住的时间也没有一个月?我现在能要求他在家陪陪我们吗?笑话!这是不可能的。当年父亲不就是这样要求我吗?可是当年父亲求孩子在身边陪坐几十分钟的愿望就变成了奢侈?唉!现在我们的孩子他为了自己的发展跑到天涯海角去打拼,如果想孩子就默默地盯住天空飞来飞去的银色机冀吧!说不定那架飞机内就坐着自己的小子呢!



golang客户端取消http请求
Tag golang, cancelable, http, on by view 3415

首先,创建带有Cancel Context的http请求

var cx context.Context
cx, req.cancel = context.WithCancel(context.Background())

if req.httpreq, err = http.NewRequest(req.method, req.url, reqbody); err != nil {
    return
}
req.httpreq = req.httpreq.WithContext(cx)
req.httpreq.Header = req.Headers
req.httpreq.ContentLength = reqbodyLength

然后,需要停止请求的时候调用req.cancel()方法

if req.cancel != nil {
    req.cancel()
    fmt.Println("running cancel...")
}

return nil

这样才能够客户端强行主动取消正在进行的http连接。就是这么简单


webp动图循环n+1的问题
Tag webp, 循环, on by view 3723

最近需要用ffmpeg将视频截取转为webp动图,但是发现截取后的视频在Chrome浏览器上打开播放循环次数不对

./ffmpeg -i xxx.mp4 -map_metadata -1 -s 640x360 -r 15 -t 00:00:02 -loop 1 xxx1.webp

以上命令中 -loop 1 指定动画循环1次,但是所生成的图片在Chrome上播放却是循环2次,同时发现截取 gif 也存在这个问题

./ffmpeg -i xxx.mp4 -map_metadata -1 -s 640x360 -r 15 -t 00:00:02 -loop 1 xxx1.gif

但是后来我发现将 -loop 指定为-1却可以截取一次循环的gif,但是 -loop -1 这个参数如果用在截取 webp 就会报错。

最后我找到了 libwebp 项目,在其压缩包目录中找到了官方的 webp 查看工具 vwebp.exe ,用它播放 webp 发现却是正常的,Chrome上播放循环两次的图用它播放只有一次。这说明是 Chrome 的bug,并非 ffmpeg 转码的问题。有人也发现了这个问题。 链接