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

Java web server http 请求的一个疑惑

  •  
  •   lbmjsls1 · 2020-06-19 18:02:38 +08:00 · 2999 次点击
    这是一个创建于 1646 天前的主题,其中的信息可能已经有所发展或是发生改变。

    事情是这样,我们现在的 web server 遇到了问题,准备用 java 重写一个,现在有一个疑惑想问大家遇到这种情况如何处理

    用户打开连接,进来一个 http 请求,按照我们技术说的,这个 http 请求必须在这个连接内返回。但是有可能这次请求加载的数据很慢,比如要读写数据库,那么问题来了:

    1.一个 http 请求必须要在这个连接内返回完成吗?不管需要多长时间?是否可以过后再同步数据

    2.如果必须要在一个连接内完成,那么一个 http 请求就对应一个线程?(不管是用线程池还是其他方案实现)

    3.如果一个 http 请求对应一个线程,是不是在 web server 领域没有所谓的数据库线程?每次操作都在当前的线程内完成

    原谅问这么入门级的问题,因为原来不是搞 web server 的,现在突然紧急,硬头皮上

    19 条回复    2020-06-21 10:53:05 +08:00
    lululau
        1
    lululau  
       2020-06-19 18:17:12 +08:00
    1. 可以不在一个连接里完成,这种叫异步接口
    2. web server 的服务端的并发模型可以是多进程、多线程、reative io 、协程、或者两种模型的混合,Java 主流是 Servlet,是多线程的模型,在不讨论线程池这样的细节的情况下,一般一个请求对应一个线程,当然你可以在这个线程里创建出另外一个线程,这也是实现异步接口的一种方式
    3. 一般数据库操作和其他逻辑是在同一个线程里的
    lbmjsls1
        2
    lbmjsls1  
    OP
       2020-06-19 18:29:13 +08:00 via Android
    @lululau 如果在请求的线程内创建新的线程异步操作,当前的请求线程是不是不能退出,要等待异步结果返回才能返回给页面吗
    kevinjaz
        3
    kevinjaz  
       2020-06-19 18:39:36 +08:00
    后台的业务处理用异步处理就行了,用线程池去执行任务也行,响应能立刻返回,那么给前端的提示可能就是正在处理,请稍后查询。
    lululau
        4
    lululau  
       2020-06-19 18:40:06 +08:00 via iPhone
    @lbmjsls1 可以退出
    lbmjsls1
        5
    lbmjsls1  
    OP
       2020-06-19 18:50:04 +08:00
    @kevinjaz
    @lululau

    如果可以退出,那么这次连接就断掉了,怎么知道像谁推送消息呢,还是说每个请求是个长连接,可以先告诉它等待处理,处理完再通知处理后的结果?
    mmdsun
        6
    mmdsun  
       2020-06-19 18:59:30 +08:00 via Android
    @lbmjsls1 消息通知可以另外用 websocket 通知。或者第三方的推送平台 Sdk 等。不用靠之前那个耗时的 http 请求来通知的。
    lululau
        7
    lululau  
       2020-06-19 19:03:10 +08:00 via iPhone
    对,连接断了,后面客户端主动再次发起新的请求来查询处理结果;你说的这种推送方式也可以,可以用 websocket 来保持独立的长连接,你的逻辑处理完了需要查找到使用的 websocket 连接来推送数据,另外一种推送是 comet,基于 http 的长连接,这个本质上其实是同步的,所以连接不能关,但是客户端可以实现异步的流程效果
    wysnylc
        8
    wysnylc  
       2020-06-19 19:05:40 +08:00
    同步异步 bionio 是看业务设计,性能优化可以使用线程池 Completablefuture 等等
    lovelife1994
        9
    lovelife1994  
       2020-06-19 21:07:20 +08:00
    tomcat nio 或者 arp 都是这样的协议,如果你的服务需要处理大量连接,但是每个连接的的负载很小,通过 IO 多路复用的方式,acceptor 线程绑定端口获取新的连接,然后将连接注册到一个或几个 selector 上,selector 通过 poll 或者 epoll 的方式管理多个连接,当 IO 事件到达时,将从连接中拿到实际的请求分配给实际的工作线程处理。这样连接和线程不是绑定。一个 http 请求肯定需要通过一个工作线程去处理,问题在于是在请求到达时分配工作线程还是连接建立时分配。BIO 的方式在连接到达时分配,每个线程管理一个连接,连接不关闭且负载不高时,这部分资源就是浪费的。NIO 是用少量的线程管理大量的连接。负载取决与实际的 http 请求而非连接数。
    kevinjaz
        10
    kevinjaz  
       2020-06-19 21:18:58 +08:00
    @lbmjsls1 正常的 http 请求是无状态的,每个请求进来带了 token 之类的消息知道客户端是谁?请求处理完,返回之后这个连接就断了。除非你是用 websocket 建立长连接,那样客户端与服务端是全双工通信的,看你的描述,是因为处理的业务逻辑时间过长,要不就长时间等待,那样用户体验不好。另一种方案是异步处理,后端找个表记录任务详情,那么有个地方可以查询这个任务结果,如果是类似导入数据的需求,可以给个提示说稍后刷新页面查询。
    lovelife1994
        11
    lovelife1994  
       2020-06-19 21:20:44 +08:00
    @kevinjaz http 现在很多为了避免开销都会用到 keepAlive,会维持连接存活一段时间,这段时间就比较尴尬。
    nicevar
        12
    nicevar  
       2020-06-19 21:22:57 +08:00
    这不是什么代码实现问题,是业务逻辑和产品设计的问题。
    kevinjaz
        13
    kevinjaz  
       2020-06-19 21:32:14 +08:00
    @lovelife1994 看服务承载量,如果用户请求量大,用 keepAlive 反而会增加服务端的压力,毕竟维持一个 tcp 就占了一个坑位,看题主的问题应该是业务处理的时间都超出了 keepAlive 的超时时间了,所以这里应该是如何设计好这个交互问题。
    Aruforce
        14
    Aruforce  
       2020-06-19 22:29:00 +08:00 via Android
    你说这个 web server 不会是 web 容器吧?
    lululau
        15
    lululau  
       2020-06-19 22:41:58 +08:00 via iPhone
    @Aruforce 和 web server 相对的说法应该是 application server, 不过很久没看到这么老土的说法了,抠字眼没意思,何况这个字眼抠的根本就是错误的,tomcat 为什么就不能叫 web server, 不支持 http 还是不能 serve 静态资源
    yyb2yang
        16
    yyb2yang  
       2020-06-19 22:43:05 +08:00 via iPhone
    按照楼主的业务需求,异步通信是一个解决办法:A 、B 两个服务(或者父子线程)的通信,B 的业务处理时长很长的时候,A 通过异步调用 B 以后,让 B 自己去处理复杂的业务,而 A 可以立即反馈给客户端一个结果,而不是阻塞等待 A 的执行结果的反馈。至于后期 B 执行成功或者失败,可以再发起一次查询请求,或者 B 执行完以后,由 B 的业务逻辑发起一个客户端的通知反馈都是可以的。
    mreasonyang
        17
    mreasonyang  
       2020-06-19 23:55:27 +08:00 via iPhone
    这和你的业务逻辑有关啊,如果客户端或前端逻辑就是同步等待响应结果,那必然是等服务端响应了才行,如果能接受异步逻辑,那就建长连或者长轮询搞成异步就可以了,当然靠用户手动刷新获取结果也是个方案。要是怕服务端出现线程和连接耗尽的问题,可以使用 nio 实现的 web 容器替代 bio 的,比如 jetty 之类的,然后调大 backlog,增加业务线程池上限就可以了。
    johnj
        18
    johnj  
       2020-06-20 06:37:10 +08:00
    说个楼上没提到的解决方案:server sent event 。可以做到一次请求长链接多次响应。浏览器端和 spring mvc 服务器端都有标准实现,细节我就不说了。
    realpg
        19
    realpg  
       2020-06-21 10:53:05 +08:00
    问题基本是你们的业务研发太菜……
    对于慢返回的连异步请求都不会
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2762 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 09:34 · PVG 17:34 · LAX 01:34 · JFK 04:34
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.