专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

这不会又是一个Go的BUG吧?

  hello,大家好呀,我是小楼。
  最近我又双叒叕写了个BUG,一个线上服务死锁了,不过幸亏是个新服务,没有什么大影响。
  出问题的是Go的读写锁,如果你是写Java的,不必划走,更要看看本文,本文的重点在于Java和Go的读写锁对比,甚至看完后你会有一个隐隐的感觉:Go的读写锁是不是有BUG?故障回放
  背景简单抽象一下:一个server服务(Go语言实现),提供了一个http接口,另有一个client服务来调用这个接口,整体架构非常简单,甚至都不用画架构图你也能够理解。
  这两个服务上线运行了一段时间都没什么问题,突然有一天client调用这个server的接口全都超时了。
  碰到这种问题,第一时间去查看日志和监控,client端全是超时日志,server端日志没有异常,甚至连请求的监控都没有上报,仿佛client端的请求没有到达server端一样。
  于是去server服务器上手动请求了一下接口,结果卡主不动,这下排除了client,一定是server端出了问题。
  这种卡死的问题其实很好查,直接用pprof看协程卡在哪里基本就能得出结论(和Java的jstack类似的工具),但这个服务没有开启pprof,只能改了代码打开pprof重新发布,等待下次问题复现。
  好在运气不错,2天后问题就出来了,用pprof看下程序卡在了哪里:
  原来卡在了一个判断集群或服务是否是小流量的地方,该接口会接受一个集群名或服务名的参数,然后判断该集群或服务是否是小流量集群,进而做一系列事,至于做了啥不重要。小流量集群是配置在配置中心中。
  我把这段代码摘出来(图中是走的判断集群分支,下面代码以更简单的服务分支讲解,底层一致)。为了避免空洞,这里我先简单讲解一下程序的逻辑:首先小流量的配置定义了一个读写锁(sync。RWMutex),以及在内存中保持了哪些服务需要灰度的规则(scopesMap)
  配置变更时调用reset刷新这个scopesMap,用写锁,后续逻辑省略
  判断是否为灰度服务,先加读锁看看规则是否存在:
  再加锁判断服务是否命中规则:
  这样圈出重点,你可能一眼就看出问题了,读锁加了两次,第二次没有必要,属于手误了。确实,删除第二个加读锁的代码就没问题了。如果事情到这就结束了,那这篇文章也没有必要写了,下面我们分析下为什么会死锁。为什么会死锁
  看到这个结果,我第一反应是Go的锁的重入性问题。
  熟悉Java的同学对锁的重入并不陌生,以防有读者不明白锁的重入性,我用一句话来概括:
  可重入锁就是可以重复进入的锁,也叫递归锁。
  Java中有一个ReentrantLock,比如这样,重复加锁是没有问题的:
  但Go里面的锁是不可重入的:
  这个坑我也踩过,这是Go的实现问题。只要你愿意,用Java也能实现不可重入锁,但Java中大多数使用的还是可重入锁,因为用起来比较方便。
  至于Go为什么不实现一个可重入的锁,可以参考煎鱼大佬的这篇文章《Go为什么不支持可重入锁?》,其原因总结起来就是Go的设计者觉得重入锁是个不好的设计,所以没有采纳。不过我觉得这篇文章的评论更精彩:
  说到这,你可能会说,上面出问题的明明是读写锁(sync。RWMutex),读写锁的特点是什么?读与读之间不互斥读与写、写与写之间互斥
  既然读锁之间是不互斥,也就是可加两次读锁,那么读锁必然是可重入的。我们写个demo测试下:
  果然如我们所想,顺便看一下加读锁的逻辑:
  看我框出的代码,如果有写锁在等待,读锁需要等写锁!
  这是什么逻辑?
  如果一个协程已经拿到了读锁,另一个协程尝试加写锁,这时应该加不了,没什么问题。如果这个读锁的协程再去拿读锁,需要等写锁,这就死锁了啊!
  为了验证,我构造了一个demo:
  这段代码按、、顺序执行,第段写锁需要等第个读锁释放,第段读锁需要等第段写锁释放,最终就是一个死锁的逻辑。
  仔细想,这里面最有争议的要属已经拿到读锁再次进入读锁需要等写锁这个逻辑。
  Java中是这样的吗?写个demo试试:
  Java一点事都没有,这是为啥?遇事不决,看源码!但Java的源码太长,又不是本文重点,所以就只说几点重要的结论:Java的ReentrantReadWriteLock支持锁降级,但不能升级,即获取了写锁的线程,可以继续获取读锁,但获取读锁的线程无法再获取写锁;ReentrantReadWriteLock实现了公平和非公平两种锁,公平锁的情况下,获取读锁、写锁前需要看同步队列中是否先线程在我之前排队;非公平锁的情况下:写锁可以直接抢占锁,但是读锁获取有一个让步条件,如果当前同步队列head。next是一个写锁在等待,并且自己不是重入的,就要让步等待。
  在Java的实现下,如果一个线程持有了读锁,写锁自然是需要等待的,但是持有读锁的线程也可以再次重入该读锁。
  我们发现Java和Go的读写锁实现不一致,这个不一致也就是导致我们写出BUG的原因。这合理吗
  抛开实现,我们思考一下这样合理吗?一个协程(或线程)已经获取到了读锁,别的协程(线程)获取写锁时必然需要等待读锁的释放既然这个协程(或线程)已经拥有了这个读锁,那么为什么再次获取读锁时需要管别的写锁是否等待呢?
  可以想象病人排队看医生,前面一个病人向医生问诊,进去后把门关上,在里面无论问多长时间(理论上)是他的权利,后面的病人在他没出来前是不能打开门的。
  但Go的实现却是,前一个病人每问完一句话得看一眼门外是否有人在等,如果有人在等,那他就要等门外的人问完才能问,但门外的人又在等他问,所以大家死锁了,谁都别想看完病。
  是不是细思下来,感觉这是不是Go的一个BUG?Go为什么这么实现
  我尝试去github上搜索了一下,发现了这个issue:
  https:github。comgolanggoissues30657
  从标题就能看出他遇到了和我一样的问题:
  Readlockingshouldnthangifthreadhasalreadyawritelock?30657
  看看里面有人是怎么回答的:
  这位大佬说,这不符合Go锁的原理,Go的锁是不知道协程或者线程信息的,只知道代码调用先后顺序,即读写锁无法升级或降级。
  Java中的锁记录了持有者(线程id),但Go的锁是不知道持有者是谁,所以获取了读锁之后再次获取读锁,这里的逻辑是区分不了是持有者还是其他的协程,所以就统一处理。
  这点其实在Go源码的注释中体现了,我也是后来才注意到:
  翻译一下是:
  如果一个协程持有读锁,另一个协程可能会调用Lock加写锁,那么再也没有一个协程可以获得读锁,直到前一个读锁释放,这是为了禁止读锁递归。也确保了锁最终可用,一个阻塞的写锁调用会将新的读锁排除在外。
  不过这个警示实在是太不起眼了,大概就是这个效果:
  这一幕像极了产品和程序员:产品经理:我要实现这个功能,怎么实现我不管Go:这破坏了我的设计原则,不接受这个功能产品经理:大家都退一步,你换个代价小的方法解决吧
  于是,程序员在读写锁上写下了一段注释:
  最后
  这个死锁的坑确实很容易踩,尤其是Java程序员来写Go,所以我们写Go代码时还是得写得更Go一点才行。
  Go的设计者比较偏执,认为不好的设计坚决不去实现,就如锁的实现不应该依赖线程、协程信息;可重入(递归)锁是一种不好的设计。所以这种看似有BUG的设计,也存在一定的道理。
  当然每个人都有自己的想法,你觉得Go的读写锁这样实现合理吗?
  如果你看完觉得有点收获,给个赞、在看吧,你的支持是我持续创作的动力
  搜索关注捉虫大师,后端技术分享,架构设计、性能优化、源码阅读、问题排查、踩坑实践。

国内已有四家银行破产,百姓存在银行的钱,是否能全部拿得回来?然而很多人却存在这样一个疑问,把钱存入银行当中就真的安全了吗?其实事实也并非如此。当然这里所说的并非如此,并不是指银行当中会出现现金被抢的情况,而这里所说的并非绝对安全,便是银行出多地限购政策松绑!地产板块再度爆发,困境反转逻辑还能演绎多久?4月6日,地产板块力挺大势。截止收盘,房地产指数大涨近3。与此同时,房地产板块大涨也带动地产产业链板块集体走强,钢铁建材等板块集体上涨。具体来看,光明地产中华企业栖霞建设中国武夷等别管工资高低,建议常喝这3种小众茶,好喝又养人,性价比高喝茶是不分年龄段的,但人过中年,往往会更喜欢喝茶。此时,茶友会愿意把时间花在喝茶上,不管是一个人独自品尝,还是与三五好友共饮都是一件很美好的事情。因为我们不仅需要物质方面的享受,同湖人无缘附加赛谁之过?以时间次序细数,三人难卸其责文水清清110121负太阳,马刺击败掘金,湖人正式无缘附加赛,苦涩的结局是怎么形成的呢?我们以时间次序细数,三人难卸其责。詹姆斯赛季场均30。38。26。2两年前夺冠时,球迷看到湖论时间的本质,时间会不会只是人类大脑的一种幻觉?时间,看起来如此简单,但它又如此复杂。如果你问身边的人时间到底是什么?很可能会得到各式各样的回答,但没有一个人能给时间一个准确的让人信服的答案。因为直到目前,科学家也并没有定义到底美媒公布NBA各队获得状元签概率火箭时隔20年再度获得状元签?北京时间2022年4月6日讯,作为世界篮坛的最高舞台,大洋彼岸的NBA联赛可谓备受球迷关注。截至目前,本赛季的NBA常规赛已经接近尾声,排名东西部前十的球队已经确定。对于一些战绩不特战荣耀人物角色关系图解析特战荣耀更新时间介绍特战荣耀首播1集,热度还是非常高的,官方也发布了人物关系图,了解整个军营状况,该剧一共45集,四月份可以播完,那么,具体更新时间是怎样的?下面小编就带来介绍。特战荣耀人物关系图追剧严为民医药板块,风险极大盘面绝大部分个股在上涨,尽管指数一点点红,但那叫星星之火可以燎原。今天印象最深的是什么?以中国医药为代表的医药最近开始低下了高傲的头颅,我还是那句话不要发灾难财。我现在再次强调,跟农村的进步与落后悖论没有人卖给下沉市场好东西下沉市场的爱好一丁点都没有改变,并与一二线城市的偏好愈发相去甚远。本文以小见大,意在通过清明返乡观察,让北上广的精英们感受一下下沉市场的生活消费真相。躺姐老家有很多个制造业工厂,工烦躁不安睡不着,常按这个穴助你一夜好眠大家好,我是素问中医宫娜医生。之前接诊过一位患者,是一位退休教师,他的失眠情况很糟糕1入睡后几乎每隔一个多小时就要醒一次2白天总是昏昏沉沉,夜里经常盗汗3平时手脚怕冷,却容易上火4腹胀怎么缓解4种消气方法让你远离腹胀腹胀是多数人都会有的症状,引起这个情况大的原因有很多,像是一直不运动,还有就是吃的食物有问题。总是有腹胀的症状,会让大家感觉非常难受,也会影响到正常的饮食,一旦出现了腹胀要怎么缓解
千亿像素看仙都黄帝缙云,人间仙都仙都,位于缙云县境内,是一处以峰岩奇绝山水神秀为特色的国家级风景名胜区国家5A级旅游景区。据北宋图经记载唐天宝七年(公元748年)六月八日,忽有五彩祥云围绕在缙云山四周,云中仙乐响饮食与心理健康的关系?专家良好饮食益处多,早了解早受益随着现代生活节奏的加快,越来越多的人们出现焦虑烦躁,饮食不规律等情况。而长时间的忽略饮食并处在高压的环境下很容易导致我们出现各种心理健康方面的问题。所以如何改善这一状况成为很多专家欧联杯曼联vs皇家社会,强势反弹的曼联能否领跑小组积分榜一边是强势反弹4连胜的曼联,一边是刚刚逼平马竞的皇家社会,一边是英超第5,一边是西甲第9,球迷们更加喜欢谁呢?昨天呢也是开心的一天,视频中拜仁凭借萨内和丹布罗西奥的乌龙球帮助球队2狂轰4775!东契奇一战封神,2亿巨头心服口服,MVP之争悬念陡升一天之前,字母哥在希腊VS乌克兰的比赛中狂轰41分,东契奇在对阵德国的比赛中砍下36分,有记者问东契奇谁会在一场比赛中得到最高分?东契奇不假思索地表示我打赌会是字母哥,为什么呢?因47岁谢晖执教成功,娶31岁俄超模,3岁混血儿子我就喜欢大连赢李白诗词中说过天生我材必有用,就是说每个人都应该保持自信,每个人都有自己擅长的能力,延伸一些,就是说我们应该主动发现自己擅长的能力,然后扬长避短,不但加强修炼自己长处,这样自然能超欧冠神剧情!10分钟3球,91分钟绝杀,96分钟绝平,101分钟真绝杀欧冠小组赛首轮,马竞主场迎战波尔图,这也是一对老冤家了,虽然马竞在实力和名气上占优,但是他们也并没有必胜的把握,因为球队在攻击线也是有所欠缺,而格里兹曼的出场时间又被严格的限制,这拜仁力助国米检验最重要新援南大王少帅一举揭出蓝黑教练短板输掉米兰德比战后,国米又02完败给拜仁,新赛季前6场比赛,输掉整整一半。输球并不令人意外,毕竟如今的国米,财力实力江湖地位欧冠搏杀经验,较之拜仁有着不止一个档次的差距,何况拜仁此前2022欧冠联赛球队排名(116)皇马再次夺冠,还是曼城有望?皇家马德里去年赢得了冠军联赛冠军,并且在过去十年中已经五次获得冠军。本赛季他们还会再夺冠吗?我根据三个不同的因素对32支球队进行了排名,它纯粹根据团队的历史结果进行排名。以下是11欧冠首轮战罢英超四支球队喜忧参半,切尔西利物浦到底怎么了欧冠首轮战罢英超四支球队喜忧参半,切尔西利物浦到底怎么了文姜诗华欧冠小组赛首轮比赛已经全部踢完,大部分强队都顺风顺水地拿下开门红,当然,也有例外甚至是冷门,本赛季参加欧冠的四支英超国乒单打11人出局!首轮结束输掉五场外战,男队仅剩三位选手抗压2022年9月8日,乒乓球WTT阿曼赛继续进行,单打首轮结束,国乒7人晋级16强,输掉五场外战,到目前为止有11人出局,男队仅剩3人,女队还有4人,就目前的情况来看,女单夺冠问题不杜兰特遭名宿嘲讽,贝弗利违心言论引争议,纳什豪言爆更衣室矛盾北京时间9月8日,自由市场正在如约而至的持续发酵中,各支球队都在跃跃欲试,地震级交易以及顶薪续约合同此起彼伏,联盟也在不断上演着抢人大战,为下赛季的球队崛起保驾护航。杜兰特再遭名宿
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网