Java web server http 请求的一个疑惑

2020-06-19 18:02:38 +08:00
 lbmjsls1

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

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

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

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

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

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

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

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

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/683128

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX