lesismal

lesismal

V2EX 第 497905 号会员,加入于 2020-07-06 13:49:58 +08:00
4C-2G 来战 [ Golang Websocket 百万连接测试 ]
程序员  •  lesismal  •  88 天前  •  最后回复来自 qbmiller
23
nbio 近期的一些功能更新,来骗点 star
  •  1   
    Go 编程语言  •  lesismal  •  156 天前  •  最后回复来自 lesismal
    2
    吃八分饱长寿与充电 85%能保护电池
  •  1   
    硬件  •  lesismal  •  2022-05-06 13:22:14 PM  •  最后回复来自 julyclyde
    22
    筑·格瓦拉
    分享发现  •  lesismal  •  2022-05-03 10:40:16 AM  •  最后回复来自 lesismal
    2
    伸手党真是烦
    程序员  •  lesismal  •  2022-02-16 01:05:48 AM  •  最后回复来自 neoblackcap
    95
    最近犯闲,想再写点啥项目,有推荐的吗?
    Go 编程语言  •  lesismal  •  148 天前  •  最后回复来自 lesismal
    44
    lesismal 最近回复了
    @netabare #188 最让人头疼的是,这玩意被当成标准答案并分享,要是不出来说一下,估计这帖子里好多人还得继续扩散下去

    @CRVV #195 实际上就是绝大多数人都是用社区的东西、看到社区的文章不管对错直接当作宝贝,然而学校里课本上教的那些基础知识才是本质、却被大多数人扔了不懂得拿出来用于独立思考。所以很多人会觉得自己八股文背得滚瓜烂熟、实际工作中却仍然各种搞不懂,只做人云亦云、缺乏独立思考、缺乏学以致用
    @xsen #161

    > static 方法或全局变量有局限性,资源、方法没封装到一起,不好管理与维护

    没看出局限性,也没看出不好管理与维护。
    @BQsummer #135

    > double check + synchronize 的单例实现在业务场景很常见啊

    这种实现常见不代表它是最好,甚至它反而把问题复杂化了,先看下我在 #119  说的

    > 比如我需要创建 apns client 去做推送, 因为推送会因为营销活动大幅波动, 所以需要运行时做扩容, client 的数量不会固定死, 运行时创建 client 就需要加锁, 几十个线程同时抢 synchronize 的锁性能很差非常多

    兄弟,既然数量不固定,咱就别拿来当成单例的案例讨论了,这个可以叫连接池或者 client 池
    @yuanmomo 故事分享是挺好玩的,但这世上每件事都有很多个维度和角度,即使是技术这种客观性强的东西也有很多维度和角度。如果只看到甚至只是总结归纳其中的一些点、把这些作为真正原因或道理,我估计这种方式总结出来的结论很多都是片面的。
    @yuanmomo #79

    首先我是觉得,初始化阶段初始化一个实例、以后都不再需要什么同步机制,这种是最节约资源的,也没什么复杂,就是自然逻辑——使用前先初始化。如果是 java 这种语言没有全局变量之类的,静态也可以,比如这个帖子里结尾部分的 “饱汉式(静态内部类)”:
    https://juejin.cn/post/7073827225509822500
    但这个帖子里,除了结尾这种实现,上面那么多种奇技淫巧的归纳总结,全都是屁一样的玩意。本来就很简单的东西、按照最简单高效的方式一步到位,非要整出这么多七七八八的垃圾实现还拿来宣传甚至作为面试题的答案去考别人。
    这么说吧,看到这个帖子之前如果有人问我这个高并发单例的面试题,首先我就懵逼——这都啥概念啊、我不会啊!然后如果再按照你的双重锁的答案来评判我,反倒是我弱鸡喽?

    同步、并发一致性、锁,这些基础的东西直接问一样可以,非要造一堆乱七八糟的概念和脱裤子放屁的模式,烦死了。最可怕的是一些脑残带头的人整理出来这些,然后你们这些面试官不管三七二十一就觉得真香然后拿来“学以致用”,连点实事求是的独立思考都没有、就把糟粕当精华了。而且这种现象非常容易出现人传人,你面试别人让别人觉得这玩意好了,候选人回头就可能也拿来去考别人,一传十十传百,人人八股滔天,让我们这些喜欢少即是多、喜欢大道至简的、还喜欢独立思考、不喜欢向糟粕低头的人步履维艰

    > 最后,针对你说的问题,我再跟你分享一个故事

    我实在想不出这种故事与我说的问题有什么直接关系。

    你对故事里的感悟是在自己的角度考量的,你没换位思考别人的角度。就比如你这故事,人家觉得没意思想离职,可能是技术没意思,可能是钱没意思,可能是多种因素综合下来没意思,甚至最简单的,外头有比你这有意思多的工作。就你一个简单的问别人能不能整理出来、他不知道,我猜你的意思是他还有进步空间所以不应该走是不是?就算人家在你这还有进步空间,跟人家想不想走有直接关系吗?就算能在你这继续进步,不代表出去别的地方就不能进步,反而可能进步得更好。甚至,人家可能觉得,在你这即使进步也就那么一点点了而且没意思、人家没兴趣去做这个整理和进步
    @yuanmomo

    #56 请教

    高并发单例我是不懂的,刚搜了下是说要线程安全之类的

    然而,static 变量、或者非 java 语言全局一个变量,或者初始化阶段全局一个创建就完事了,何苦还要同步机制额外的锁处理高并发的线程安全困扰?多了一点锁开销虽然不大但也是每次调用都浪费一丢丢性能的啊

    所以,考这个题目有意义吗?

    单例模式被归纳成一种设计模式,还能入选到八股文里,实在搞不懂出题的人们都是怎么想的
    #115

    man 手册离的是那是 `The suggested way`, 因为 ET 数据没读完、没有新数据到来是不会再触发可读的、如果用户解析处理逻辑有 bug 并且不继续读和处理就可能僵尸连接了。
    但请你看清楚,那 `You must do it`,所以 `必须要读到返回 errno=EAGAIN` 这说法也是不准确的。

    ->

    man 手册里的那是 `The suggested way`, 因为 ET 数据没读完、没有新数据到来是不会再触发可读的、如果用户解析处理逻辑有 bug 并且不继续读和处理就可能僵尸连接了。
    但请你看清楚,那不是 `You must do it`,没人强迫你必须读到 EAGAIN ,也不是只有 event loop 里才能进行读、其他地方就不能读了,所以 `必须要读到返回 errno=EAGAIN` 这说法也是不准确的。
    > 1. man 7 epoll 说的很清楚
    > The suggested way to use epoll as an edge-triggered (EPOLLET) interface is as follows:
    > a) with nonblocking file descriptors; and
    > b) by waiting for an event only after read(2) or write(2) return EAGAIN.
    > 你必须要读到返回 errno=EAGAIN ,如果我是水平模式,不没必要

    > 2. 你这属于抬杠,在多少情况是 socket 缓冲区暴满了?要考虑大部分情况的代码分支走向
    > 1 )程序处理的慢,读不过来,堆积了
    > 2 )业务类型就是客户端不停的 send (也得看处理的快慢)
    > 这都不是关键问题,多路复用同步的读取 你可以把 recv buf 搞得很大,因为它是共享的,不存在浪费内存的问题。

    你好像没看懂我在说什么,我说的是你当前这个 LT 单次 event 不读完的实现,与 ET 单次读完的区别,我没说你 bug 啊也没说你一定需要读完啊,你再看下 #111

    > 你这属于抬杠,在多少情况是 socket 缓冲区暴满了?要考虑大部分情况的代码分支走向

    单次读完相比于你当前的 LT 单次不一定读完,也并不多浪费什么代码,也就循环里多个 if EAGAIN 的判断。而且我也没说必须这样做,只是分析你的 LT 实现与 ET 的区别,没说你这个实现就影响性能了或是怎么样,而是说 `这样未必最优`,我可不是说你这样一定不如单次读完啊。

    你这么容易觉得我在抬杠,没必要。人在觉得发现新大陆、搞了大进步的时候最容易自我陶醉、也最容易听不进去跟自己不同的观点,很正常。
    你可以先让自己冷静下来再看看,或许能吸收些新东西。


    另外:
    > 你必须要读到返回 errno=EAGAIN

    并不是必须这样的,自己想做流控的话,可以自行选择读多少、什么时候继续读,比如 golang ,有数据来了 net.TCPConn 就可读,但是你应用层没有调用 net.TCPConn.Read 也没关系啊,你什么时候想读直接能读就醒了,如果当前没数据不可读、阻塞在那等 runtime event 来了唤醒就可以了

    man 手册离的是那是 `The suggested way`, 因为 ET 数据没读完、没有新数据到来是不会再触发可读的、如果用户解析处理逻辑有 bug 并且不继续读和处理就可能僵尸连接了。
    但请你看清楚,那 `You must do it`,所以 `必须要读到返回 errno=EAGAIN` 这说法也是不准确的。


    > 另外,我回复中写的是 EAGAIN ,不是 EINTER 。

    我上面说的不是你回复其他人的 EAGAIN ,而是说你源码中的这块,我上面漏贴了代码链接:
    https://github.com/shaovie/niubix/blob/main/src/io_handle.cpp#L24

    > 3. 是不完整,我惊讶的是给我的性能发挥空间还很大啊,如果只是性能超过 haproxy 20%,那我就没啥好惊讶的,等我完善 完善 可能这 20%就被抹平了

    那可以惊讶的事情可真是太多了,你继续加油提高性能天花板吧

    > 我说的读的情况多一次 syscall ,指的是 read ,不是 epoll_ctl

    读的时候,ET 怎么就可能比 LT 多一次 syscall 了呢?同样一次读事件到来,同样的 read buf 。
    你是不是又搞混了什么。。
    #111 编辑的时候窜行了,更正下

    > 这个可能不太准确:Edo while 条件+ONESHOT 需要重新添加事件,这种才会需要更多 syscall ,如果都是单次读完当前数据的话,ET 和 LT 是一样的。

    更正为:

    > 这个可能不太准确:ET+ONESHOT 需要重新添加事件,这种才会需要更多 syscall ,如果都是单次读完当前数据的话,ET 和 LT 是一样的。
    @shaoyie #97

    > 减少 syscall 次数,而且有了这个前提 大部分情况 ET 模式反而会浪费一次 syscall

    这个可能不太准确:Edo while 条件+ONESHOT 需要重新添加事件,这种才会需要更多 syscall ,如果都是单次读完当前数据的话,ET 和 LT 是一样的。

    > 而在水平模式下,读出来的数量小于你的 buf 长度 就可以了,不需要再尝试一次

    除非你的读 buf 长度大于 socket 设置的读缓冲区 size ,否则不管 ET 还是 LT ,读本身是没法保障单次读出来的数量小于 buf 长度的,因为有可能 socket 读缓冲区数据量大于读 buf 长度

    这里的 do while 条件也不是尽量读完,例如 socket 读缓冲区有 33k 数据,buf 是 32k ,本次读到 32k ,则不满足你的 (ret == -1 && errno == EINTR) 条件。
    但这也不能算 bug ,因为你默认用的是 LT ,即使本次没读完、下一轮 event loop 也会继续触发读,只是相比于单次读完,这样需要内核在下一轮 event loop 继续派发可读事件、这样未必最优。

    > 庆幸,昨晚程序能跑起来后我就第一时间做了对比测试,数据很惊讶

    niubix 实现的功能本身就不是完整 http 相关功能、比 nginx 、haproxy 的逻辑少很多,所以比它们快也应该是意料之中,OP 为此惊讶这件事让我感到狠惊讶!
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1919 人在线   最高记录 5930   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 16:18 · PVG 00:18 · LAX 09:18 · JFK 12:18
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.