ARTS 第十周: 找中位数,quic,nginx DNS的坑,

一、Algorithm

find-median-from-data-stream(hard)

Median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value. So the median is the mean of the two middle value.
For example,
[2,3,4], the median is 3
[2,3], the median is (2 + 3) / 2 = 2.5
Design a data structure that supports the following two operations:
void addNum(int num) - Add a integer number from the data stream to the data structure.
double findMedian() - Return the median of all elements so far.

解法一:维护一个列表,插入的时候直接放到列表最后面,然后再取中值前sort一把,排序的复杂度是O(nlogn)。这种解法时间复杂度太高了,过不了。

解法二:在add的时候排序,使用二分查找法,找到待插入的位置,然后插入。二分查找的时间复杂度为O(logn),插入由于需要后移元素,所以复杂度为O(N)
代码如下:

class MedianFinder:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.nums=[]
        

    def addNum(self, num: int) -> None:
        low=0
        high=len(self.nums)-1
        while (low <= high ):
            mid = (low+high)//2
            if (self.nums[mid]>num):
                high=mid-1
            else:
                low=mid+1
        self.nums.insert(low, num)
                

    def findMedian(self) -> float:
        l=len(self.nums)
        if l%2 != 0:
            return float(self.nums[l//2])
        else:
            return (self.nums[l//2]+self.nums[l//2-1])/2

解法三:使用大小堆
python有一个内置的数据结构堆heapq,特性是a[k]<=a[2k],a[k]<=a[2k+1],我们利用的就是堆的有序性,插入任意的数据,都能保持有序。我们创建两个堆,其中一个大顶堆,一个小顶堆。大顶堆最大的值在上面,小顶堆最小的值在上面。我们只需要控制大顶堆最大的值小于或等于小顶堆最小的值,同时大顶堆的大小==小顶堆的大小,或者只多一个。

大顶堆的实现:通过将整数取负值,排序出来,最小的值最后取负值,也就是大顶堆的最大值了。

class MedianFinder:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.maxHeap = []
        self.minHeap = []

    def addNum(self, num: int) -> None:
        heapq.heappush(self.maxHeap, -num)
        tmpMax = self.maxHeap[0] if self.maxHeap else 0
        tmpMin = self.minHeap[0] if self.minHeap else 0
        if (-tmpMax > tmpMin) or (len(self.maxHeap) > len(self.minHeap)+1):
            heapq.heappush(self.minHeap, -heapq.heappop(self.maxHeap))
        if len(self.maxHeap) < len(self.minHeap):
            heapq.heappush(self.maxHeap, -heapq.heappop(self.minHeap))

    def findMedian(self) -> float:
        if len(self.maxHeap) == len(self.minHeap):
            return (-self.maxHeap[0]+self.minHeap[0])/2.0
        else:
            return -self.maxHeap[0]*1.0

二、Review

Quic在Uber中的应用

点评

http2在网络质量变化比较大,丢包严重的情况下,质量也不好 。TCP针对这种场景,受限于协议规则,并没有太好的解决方法,只能寻求他路:Quic。Quic已经是http/3的标准了。
在Uber的实践中,使用了Quic能降低客户端10-30%的https延时。

改造的话,需要整条链路,从客户端到服务端,在Uber的应用中,并没有提到CDN,说明他们动态的域名并没有上CDN,也有可能这仅仅是测试环境。考虑都Uber需要传输的都是数据类,基本上没有媒体数据(图片,视频)等。所以不上CDN也是可行的。

腾讯在内部也已经开始灰度上线Quic了,腾讯走的路可能会曲折一点,尤其是在服务端的Quic实现上。Quic协议在开源的项目中有很多的实现,比如google独立出来的gQuic项目,从chrome项目中独立出来了,但是已经很久没有更新了,目前使用的chrome/quic中的源码,但这里面坑也比较多,具体的可以看https://myslide.cn/slides/9804

腾讯不单在腾讯云上开放了Quic,在内部也和各业务有合作,在CDN上实现了Quic,用于静态加速。

三、Tips

Nginx DNS的用法与坑(https://www.nginx.com/blog/dns-service-discovery-nginx-plus/)

Nginx作为反向代理,我们有可能需要将某些接口转发到特定的域名,比如这个样子;

location /api {
     proxy_pass http://xxx.example.com/api
}

在正常情况下是没有问题的,但是如果xxx.example.com的解析发生了变化,nginx是不会自动去刷新的,这就可能导致故障。比如需要切换服务器,原来的服务器切换DNS后可能会停服,但我们的nginx这边还是解析到老的服务器。这和Nginx的机制有关,针对有域名解析的,默认情况下,nginx会在重启或热加载的时候检查解析,如果这个时候解析失败,或导致nginx启动失败或者热加载失败。而一旦启动后,域名的解析结果将一直缓存在内存中,直到下一次重启或者热加载。

如果要使用dns解析的话,需要显示的配上resolver,然后将域名写入到变量中。

resolver 8.8.8.8 valid=60s;
location /api {
     set $domain "xxx.example.com";
     proxy_pass http://$domain;
}

这样就可以解决上面的问题。

但如果我们proxy_pass希望转发到多个域名,比如实现自动容灾与切换,使用upstream后,如果第一个sever返回异常,则会自动到下一个server重试,这样可以达到自动容灾的目的。那用法应该是这样的:

upstream backends {
    least_conn;

    server backends.example1.com:8080 max_fails=3;
    server backends.example2.com:8080 backup;
}

server {
    location / {
        proxy_pass http://backends;
    }
}

但这种情况下,同样存在dns无法刷新的问题,那解决方案呢?

resolver 8.8.8.8 valid=10s;

upstream backends {
    least_conn;

    server backends.example1.com:8080 max_fails=3 resolve;
    server backends.example2.com:8080 backup resolve;
}

server {
    location / {
        proxy_pass http://backends;
    }
}

但是可惜的upstreamre的resolve这个功能,开源版本的nginx是没有的,只能购买nginx plus版本,价格不便宜。我暂时没有找到其他方案,如果各位其他的解决方法,欢迎联系我,谢谢!

四、Share

分享一次故障定位的过程。
我们有一个拨测系统,这个系统会针对提供http服务的域名进行拨测,用于监控域名是否有异常。现象是我们部署在新加坡的一个探测点,拨测全球其他域名,成功率都很低,连拨测新加坡本地域名成功率都只有80-90%左右,这很不正常。这导致频繁告警,联系到拨测负责人说是新加坡本地网络查,从常识来看,这很不正常。新加坡的网络建设不会比中国差,偶尔的网络质量波动是可能的,但是持续性这样差,绝对不可能。

继续分析失败的请求状态码返回码是28,正常的请求状态码是0。我们看下ret code 28代表啥含义。

CURLE_OPERATION_TIMEDOUT (28)
Operation timeout. The specified time-out period was reached according to the conditions.

一开始以为是建链失败,然后用mtr排查,发现并没有问题。然后用curl来模拟拨测的过程,但没有发现返回码为28的情况。

#!/usr/bin

url='http://xxx.com.cn'
for i in {1..100}
do
    curl  $url -o /dev/null
    echo $? >> /tmp/ans
    sleep 0.5
done
exit 0

想到curl的话,有可能会配置超时时间,了解到拨测那边确实配置了超时时间:5秒。然后再curl命令中加上超时的参数:--connect-timeout 5
然后就果真能复现了,复现后然后抓包:
tcpdump -i any -v host dstIP -w /tmp/capture.pcap &

查看抓包文件发现竟然没有报错,但是发现两次请求之间的间隔有异常,命名是sleep0.5秒,但是从抓包文件来看,有些请求之间竟然间隔了5S. 而且从抓包看,源和目的之间并没有丢包问题。


抓包.png

这,感觉很诡异,不正常。然后改下测试脚本,在出错的中断。

#!/usr/bin

url='http://xxx.com.cn'
for i in {1..100}
do
    curl --connect-timeout 3 $url -o /dev/null
    if [ $? == 28 ]
    then
        exit 1
    fi
    echo $i
    sleep 0.5
done
exit 0

发现具体的报错信息:

curl: (28) Resolving timed out after 3001 milliseconds

原来是dns解析的问题!难怪,抓包的没有丢包,同时间隔有5秒,这些现象都能解释得通了。然后抓dns的包,发现和nameserver会有建链失败的场景。(理论上都是同一个网段的服务器,理论上不应该有问题的,这里让系统组继续排查了,未完待续)(补充:原因是dns服务器同时配置了阿里云的slb和2台ecs,slb的后端是2台ecs,同时使用的话,在并发量请求比较大的情况下,如果使用4层协议,可能导致串包)

这里有个细节,为什么curl不带connect-timeout参数就没有报错呢,因为没有报错的话,curl自己会一直重试dns解析,直到成功或者最终超时。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266