V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
golangLover
V2EX  ›  Java

Spring 应对 IO 密集 的 web 业务系统有什么成熟的做法

  •  
  •   golangLover · 2021-08-22 12:50:23 +08:00 · 5129 次点击
    这是一个创建于 1187 天前的主题,其中的信息可能已经有所发展或是发生改变。
    也写了 spring 一段时间,发现 spring 比起 event-loop 的相关框架再处理 io 密集或者并发多的时候是性能比较低。
    查了相关的资料,总结出来就是

    1: 直接换 vert.x,但是语法以及生态完全不同。
    2: 上 spring cloud, 注册多个服务硬抗
    3:spring webflux, 好像是改了 controller 那边的方法以及拦截器之类的,但其他可以复用?不知道有没有坑,以及生态怎么样
    4: 多用 servlet3 返回 callable,复用处理线程。

    大家在业务里最主要用的是 2 吗?其他是不是都有坑或者生态问题? 想请大家指教一下。谢谢
    44 条回复    2021-08-24 10:01:25 +08:00
    zoharSoul
        1
    zoharSoul  
       2021-08-22 13:45:05 +08:00
    一般是多个机器硬抗. 反正服务水平扩展简单
    EscYezi
        2
    EscYezi  
       2021-08-22 16:18:45 +08:00 via iPhone
    没上 spring cloud,用的是 k8s 那一套
    ikas
        3
    ikas  
       2021-08-22 16:23:00 +08:00
    这与 spring 有啥关系...网络 io,高性能 spring 那也是用的 netty....
    你只要选择一个高性能网络 io,比如 netty,然后再选择一个异步模型就完事了
    ikas
        4
    ikas  
       2021-08-22 16:31:54 +08:00   ❤️ 1
    1.选择 vert.x,那就是给你全套方案
    2.选择用 spring webflux 作为 web 接口.那它就是解决了网络接入这一部分
    3.选择用 servlet3,然后使用 netty 或者 Undertow 作为 servlet 实现,同上
    2,3 你还要继续选择用啥异步框架.用 spring 的还是其他的..各种各样

    至于 spring cloud,那是架构.而不是上面的 1,2,3.....
    leonme
        5
    leonme  
       2021-08-22 16:38:08 +08:00 via iPhone
    瓶颈不在 DB 在 spring 框架? 加个缓存轻轻松松单机 4000 QPS 啊
    wellsc
        6
    wellsc  
       2021-08-22 18:28:17 +08:00 via iPhone
    @leonme 啥配置啊
    golangLover
        7
    golangLover  
    OP
       2021-08-22 18:32:00 +08:00
    @ikas 不是太懂 这句话:“2,3 你还要继续选择用啥异步框架”,难道 spring webflux 不就是异步的框架了吗?你的意思是例如使用远程 api,应该使用非阻塞 http 的客户端这种?
    golangLover
        8
    golangLover  
    OP
       2021-08-22 18:33:19 +08:00
    @EscYezi 就是增加 cluster,使得服务实例增加吗? 这个主要的优势是?
    golangLover
        9
    golangLover  
    OP
       2021-08-22 18:44:58 +08:00
    @leonme spring 单机应该就是 500 左右吧,预设就是 200 个线程。 应该说瓶颈是在 spring 的 jdbc 连接吧。其实是看了这个: https://www.techempower.com/benchmarks/ 。Spring 在 IO 密集的 fortunes 评分,跌到 300 位以外,比起 node.js 甚至 php 的都低很多。。。。。。
    ikas
        10
    ikas  
       2021-08-22 18:54:45 +08:00
    @golangLover spring webflux 是一个异步的 web 框架,它异步采用的模型是 Reactor...
    但是你 web 层之后的代码.不一定就是选择采用 Reactor 异步的模型,毕竟 java 有那么多异步库可以用
    比如你可以用 spring 的 project reactor,也可以用 rxjava,甚至用 jdk 内置的 CompletableFuture
    再往下说那就更烦了..比如 jdbc 要选啥样的异步库...如果用到 httpclient 又要选择啥样的....
    golangLover
        11
    golangLover  
    OP
       2021-08-22 18:59:59 +08:00   ❤️ 1
    @ikas 但是 rxjava 在后端很少有落地的吧,基本上都是安卓前端用的。我觉得可行的第一就是 completablefuture,毕竟内置的,也必须要用。其他要再研究一下。谢谢
    alamaya
        12
    alamaya  
       2021-08-22 20:01:02 +08:00
    golanglover 干嘛搞 java,直接换 golang 不就行了吗
    chendy
        13
    chendy  
       2021-08-22 20:25:26 +08:00
    1. 招人成本高
    2. 不需要 Spring cloud,直接加机器就行
    3. 不能同步写的异步都太难受了,业务逻辑堆起来之后更加难受
    4. 没玩过,目测也不太行
    notejava
        14
    notejava  
       2021-08-22 20:38:18 +08:00
    这个锅,spring 不背
    leonme
        15
    leonme  
       2021-08-22 20:57:58 +08:00
    @golangLover 简单看了下评测项目(解析 json 、返回 plain text 、数据库连接等等),从测试结果来看,一般非 GC 语言优于 GC 语言,复杂框架优于简单框架,比如 servlet 优于 spring,如果只是简单的 echo,那 servlet 肯定优于封装度高的 springmvc,但实际项目谁会直接用 servlet 呢?还有 spring 中使用了封装度很高的 JPA,性能肯定不如直接用 JDBC 啊。。。所有这种评测项目看看就好了,没太大借鉴意义
    yazinnnn
        16
    yazinnnn  
       2021-08-22 21:05:50 +08:00
    业务简单就换 vertx+kotlin
    EscYezi
        17
    EscYezi  
       2021-08-22 22:05:31 +08:00 via iPhone
    @golangLover 优势就是一楼所说,水平扩展简单,对于一般的 http 服务加机器就够了
    leonme
        18
    leonme  
       2021-08-22 22:27:37 +08:00 via iPhone
    @leonme 所以
    pengtdyd
        19
    pengtdyd  
       2021-08-22 22:30:55 +08:00
    花了非常大的力气选语言,选框架,选技术,远远没有多买一台服务器来的实在。
    golangLover
        20
    golangLover  
    OP
       2021-08-22 23:47:08 +08:00 via Android
    @leonme #15 @chendy #13 那 java 不那么难受的异步应该怎么写。我看 completablefuture 都 50 多个方法。。。。厉害
    golangLover
        21
    golangLover  
    OP
       2021-08-22 23:49:46 +08:00 via Android
    @leonme #15 @yazinnnn #16 @pengtdyd #19 一台新机器就并发几百。。。那得加多少啊。。
    golangLover
        22
    golangLover  
    OP
       2021-08-22 23:51:02 +08:00 via Android
    。。。。我发现这新的 V2EX 客户端用不惯都,@错了。大佬无视就好
    chenqh
        23
    chenqh  
       2021-08-23 00:42:59 +08:00
    并发几百确定不是数据库的问题吗?
    cubecube
        24
    cubecube  
       2021-08-23 01:35:23 +08:00
    如果是后台的网络 io,其实用 netty 就是事件驱动的
    如果是磁盘 io,那么用啥都一样,linux 下磁盘的 aio 也就那样。

    如果你觉得线程切换太消耗资源,那么 future 拿到后,使劲回调也一样。。
    chenshun00
        25
    chenshun00  
       2021-08-23 08:32:49 +08:00   ❤️ 1
    不管是不是 WEB 业务系统,绝大部分都是 IO 密集型的业务。

    瓶颈首先不在相关框架,Spring 只是提供了一个平台,你要异步也可以上异步,如果 DB/Cache/RPC 是同步的,框架提供的异步就没有意义了。

    没去过大厂,不知道大厂是怎么做的,我的做法加个 cache 。哪里是瓶颈其实大家心里都有点 B tree,只是改不动撒。

    1. DB(慢 SQL,机器配置,CPU...etc)
    2. 链路长(网络 IO)
    3. 相关链路服务抖动(GC/CPU 高...etc)
    4. 代码写的糙(上千次的同步 RPC 调用)
    5. 框架配置(Redis 配置导致频繁 TCP 握手挥手,全部等待链接。SQL 等待链接)
    5. 机器配置不行

    思考这些比起整异步更实际一点,大厂忽略我。
    leonme
        26
    leonme  
       2021-08-23 08:56:18 +08:00 via iPhone
    @golangLover 你看下它这个机器配置啊,才 2core
    wupher
        27
    wupher  
       2021-08-23 09:08:08 +08:00   ❤️ 1
    web flux 包括 RxJava 在 IO 密集场景也会有 back pressure 的问题。

    由于 event 过多,超出 reactor 出现能力,会出现 event 被丢弃的情况。

    对于这个问题,我觉得无论采用哪种解决方案,服务的无缝伸缩能力都是更重要的。
    jorneyr
        28
    jorneyr  
       2021-08-23 09:10:50 +08:00
    负载均衡呗,多搞几台机器就可以了。
    zmxnv123
        29
    zmxnv123  
       2021-08-23 09:21:57 +08:00
    我们的对象存储 gateway 是 spring
    zhang77555
        30
    zhang77555  
       2021-08-23 09:38:33 +08:00   ❤️ 1
    @golangLover 到底是数据库性能瓶颈还是 web 请求处理瓶颈, 数据库瓶颈可以优化 sql 优化数据库架构, 没法优化了就加个消息队列搞异步处理, web 请求瓶颈水平扩展就行了吧
    ljzxloaf
        31
    ljzxloaf  
       2021-08-23 10:46:07 +08:00
    fashionash
        32
    fashionash  
       2021-08-23 10:49:29 +08:00
    @golangLover 意思是你需要把你代码里的 rpc 、jdbc 等等一系列的都更换成异步模型,否则只是部分异步(要是较真的话也不算全链路的异步了)。但理论上来说,如果能解决网络 web 部分的异步,底层 jdbc 什么的性能上来说也能满足。就看你实际业务对异步的真实需求了
    ps.我自己有个类似网关的 java 服务就用 webflux 异步化了 web 层,底层用的都是同步的,平响上也有提升
    eric96
        33
    eric96  
       2021-08-23 10:55:33 +08:00
    netty+event loop 自己写
    glfpes
        34
    glfpes  
       2021-08-23 11:52:59 +08:00
    处理 IO 密集的场景要靠架构设计,比较常见的是把 IO 密集的工作抽象成一个微服务,把 IO 工作外包出去独立维护。
    BBCCBB
        35
    BBCCBB  
       2021-08-23 12:13:00 +08:00
    不知道淘宝的 rxjava 异步化进展到哪里了...
    dqzcwxb
        36
    dqzcwxb  
       2021-08-23 14:25:25 +08:00
    用 Completablefuture 代价最低,在任何框架都可以使用
    X0ray
        37
    X0ray  
       2021-08-23 16:51:18 +08:00   ❤️ 1
    IO 密集型应用的优化就是两个方向,异步和批量。
    异步用 completable future 就挺好。
    批量主要是接口协议的变化,比如以前 rpc 对象传参,可以把请求响应都改成 list,这样减少调用次数和 rtt.
    golangLover
        38
    golangLover  
    OP
       2021-08-23 22:46:01 +08:00 via Android
    @ljzxloaf #31 这个之前看过,一时间把它忘了。哈哈
    golangLover
        39
    golangLover  
    OP
       2021-08-23 22:47:49 +08:00 via Android
    @fashionash #32 webflux 的坑大吗,改造 spring 的会不会很麻烦,想预估一下时间。谢谢
    golangLover
        40
    golangLover  
    OP
       2021-08-23 22:48:04 +08:00 via Android
    @glfpes #34 这也算一个办法。谢谢
    golangLover
        41
    golangLover  
    OP
       2021-08-23 22:48:33 +08:00 via Android
    @dqzcwxb #36 现在在用了,不过并发连接的吞吐量还是太低
    golangLover
        42
    golangLover  
    OP
       2021-08-23 22:50:07 +08:00 via Android
    @zhang77555 #30 是 web 并发吞吐量觉得不是太好。
    golangLover
        43
    golangLover  
    OP
       2021-08-23 22:52:37 +08:00 via Android
    @X0ray #37 如果有 async await 就最好了,cf 现在还是用的有点辛苦
    ming159
        44
    ming159  
       2021-08-24 10:01:25 +08:00
    String contenxt = File.read("path");

    首先,代码运行是在 CPU 中执行;

    其次,CPU 再向 IO 设备,例如内存,硬盘,或者网络发出读写指令;

    最后,CPU 等待 IO 设备反馈;

    此时,问题就来了,CPU 以纳秒为单位, 无论是内存,硬盘,还是网络,尤其是网络 以毫秒为单位,相差了多少倍,请自行换算.

    就意味着,如果是同步 IO 模型,那在 CPU 发出读写指令后,会令当前线程

    阻塞

    阻塞

    阻塞

    等收到设备数据后,当前线程,继续往下执行.

    针对以上过程,所谓的 "性能" 也好 "吞吐量" 也好,绝大多数浪费在了 CPU 等待 IO 设备反馈的时间上了. 那如何解决这个问题呢?
    请自行阅读 https://segmentfault.com/a/1190000039898780 5 种 IO 模型.

    所以如果想提高 "性能" 两方面入手:

    1. 框架本身基于更高效的 IO 模型
    2. 业务层代码,也使用更高效的 IO 模型库:

    比如将 同步 IO 代码

    String contenxt = File.read("path");
    更换为 异步 IO 代码
    String contenxt = await File.read("path");

    async / await 都是语法糖,语法糖,语法糖,只是语言层面为了开发人员简化书写提供的便捷写法. 而底层 IO 机制无外乎前文中的 5 种 IO 模型
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1345 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 45ms · UTC 23:47 · PVG 07:47 · LAX 15:47 · JFK 18:47
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.