背景
谈到http client,可能大多数想到就是apache的那个http client 或者jdk自带的urlconnection,也许有人会考虑使用netty
无论如何,jetty的高性能实现总归是让人感到好奇,接下来我们一探究竟
样例
我们结合样例代码具体分析
httpClient = new HttpClient();
httpClient.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
httpClient.setMaxConnectionsPerAddress(10);
httpClient.setThreadPool(new QueuedThreadPool(20)); // max 20 threads
httpClient.setTimeout(5000); // 5 seconds timeout; if no server reply, the request expire
httpClient.start();
ContentExchange exchange = new ContentExchange(true) {
@Override
protected void onResponseComplete() throws IOException {
if (getResponseStatus() == 200) {
String content = getResponseContent();
System.out.println(content);
}
}
@Override
protected void onExpire() {
System.out.println("time out");
}
};
exchange.setMethod("GET");
exchange.setURL("http://127.0.0.1:8080/simple?id=x");
httpClient.send(exchange);
代码分为两段
-
初始化:设置httpclient
- 运行:实例化ContentExchange,定义callback,本例定义了两个常用的callback:onResponseComplete 和onExpire,更多的callbac可参考官方文档
-
APP在调用httpClient.send(exchange);后不会象往常一样等待返回而是立即返回, 如果有结果或者超时会通过上面的callback通知到APP
httpclient的原理及实现
1 )httpclient的模型
- SelectConnector: 作为一个connection管理器,封装了selector和connection
-
HttpDestination:一个host的抽象一个HttpClient会连接到多个HttpDestination
- HttpExchange:一次http请求的封装,一个HttpDestination会有多个HttpExchange以及多个AsyncHttpConnection
- AsyncHttpConnection:HttpClient对某个HttpDestination的一个网络连接,底层包含一个对应的socket, 可复用来完成多次请求, 如果空闲太久会被废弃
- SelectChannelEndPoint:socket的封装,AsyncHttpConnection和SelectChannelEndPoint一一对应, 但AsyncHttpConnection承载了更多的东西
- HttpGenerator:生成http request,在jetty server中负责生成http response
- HttpParser: 解析http response, 在jetty server中负责解析http request
- ThreadPool: 线程池,httpclient需要使用线程池配合完成无阻塞IO,这个会在后面的httpclient整体架构分析中详述
-
Timeout:一个已时间排序的链表结构,链表中存储需要过期执行的task,这个会在后面流程分析详述
2)httpclient的整体架构
http client 分为3组线程配合完成
-
selector线程组:数目可设置,默认为1,从_change队列中获取socket注册并扫描操作系统级别的网络事件, 通常是socket可读, 可写的信息,一旦发现有socket可读写,会将相关socket任务丢入_jobs队列供worker线程执行
-
worker线程组:数目根据并发的情况决定,从_jobs队列获取任务,如果任务阻塞会丢入_changes队列异步等待通知再干活
-
tick线程:数目1个,专门用于监控超时的请求以及空闲太久的连接
- 所有的线程都来自线程池,所以线程池最小为3,否则无法work
3)典型的场景分析
模拟一次请求
3.1 )httpclient初始化
- 1-2设置两个超时链表,一个是超时请求链表,一个是超时连接链表
- 3 启动httpbuffer
- 4 启动线程池
- 5 启动SelectConnector,此时会启动selector线程任务
- 6 启动tick线程任务
3.2)jetty http client runtime
3.2.1)httpClient.send(exchange)到底干了什么
- 1-2正如样例代码所示,APP设置HttpExchange,然后httpclient的send方法
- 2.1-2.2 httpclient根据httpexchange获取对应http destination,并调用其send方法
- 2.2.1 将次请求加入请求超时链表
- 2.2.2 - 2.2.3 获取空闲连接,如果没有,则产生一个新的连接,并调用select进行注册,否则直接使用该连接,并将此连接丢入 _jobs队列让worker线程完成请求
- 此时客户端就这样无阻塞的完成了
3.2.2)select线程如何参与这个场景
- 1-3 selector线程从_change队列获取到新的socket, 开始实例化SelectChannelEndPoint
- 4 通知http desination连接完成,于是http detination将次连接丢入连接超时链表
- 5-6 将此连接/请求丢入_jobs队列供worker线程使用
- 其实在selector线程内部还有一个该死的任务来处理空闲太久的socket,这个其实和tick线程有些重复了,我想这主要是因为jetty http client复用jetty server中select的结果
3.2.3)worker线程又如何参与这个场景
-
worker线程从队列中获取任务
- 1.1 通过此连接发送请求,请求内容http generator产生
- 1.2 一发完请求立即通过http parser读取响应,如果服务器够快,通常会读到响应
- 1.3 如果服务器不能及时响应,那么调用SelectChannelEndPoint的updateKey。向select更新此时感兴趣读, 并等待select异步通知
- 此时worker线程并不会阻塞等待服务返回,而是返回到线程池中去完成别的请求任务
3.2.4)tick线程又干了什么
-
轮询两个链表_timeoutQ、_idleTimeoutQ,没啥事休眠200ms
-
请求超时链表_timeoutQ
- 1 从链表中删除自己
- 2 执行链表取出的task,一个http exchang中匿名内部类实例
- 2.1 执行APP 定义的callback: onExpire函数
-
2.2 http desination专门维护一个exchange list来跟踪进行中的请求,此时调用其exchangeExpired, 删除list中该请求(可能此时list并没有该请求)
- 2.3 关闭连接
-
连接超时链表_idleTimeoutQ
- 1从链表中删除自己
- 2 关闭连接
- http desination 维护了两个list:_connections和 _idle,前者跟踪该host的所有连接, 后者跟踪该host的所有空闲连接,此时也会从这两个list删除连接
小结
从jetty http client应该能感知到一个高性能的客户端的某种设计模式
- worker 线程异步干活,使得app线程无阻塞,app线程通常在web 应用中也是一种服务线程,所以无阻塞特别重要, 想想在jetty server中使用jetty client的场景
-
select 线程通知网络ready事件,使得worker线程无阻塞,如果没有select线程,worker线程也失去了意义, 对于app线程来说无非是压力堆积到了worker线程这边,worker线程迟早是瓶颈
- tick线程,一种解决超时问题的设计
但这种模式未必适合那种性能很好且稳定的cache server,比如redis,memcache之类,如果后端处理够快, 少量线程甚至单线程+队列都能work,但无论如何比起常规的连接池模式强了不少
分享到:
相关推荐
赠送jar包:jetty-client-9.4.43.v20210629.jar; 赠送原API文档:jetty-client-9.4.43.v20210629-javadoc.jar; 赠送源代码:jetty-client-9.4.43.v20210629-sources.jar; 赠送Maven依赖信息文件:jetty-client-...
赠送jar包:jetty-client-9.4.11.v20180605.jar; 赠送原API文档:jetty-client-9.4.11.v20180605-javadoc.jar; 赠送源代码:jetty-client-9.4.11.v20180605-sources.jar; 赠送Maven依赖信息文件:jetty-client-...
jetty-client-6.1.6rc0.jar
赠送jar包:jetty-client-9.4.11.v20180605.jar; 赠送原API文档:jetty-client-9.4.11.v20180605-javadoc.jar; 赠送源代码:jetty-client-9.4.11.v20180605-sources.jar; 赠送Maven依赖信息文件:jetty-client-...
赠送jar包:jetty-client-9.4.43.v20210629.jar; 赠送原API文档:jetty-client-9.4.43.v20210629-javadoc.jar; 赠送源代码:jetty-client-9.4.43.v20210629-sources.jar; 赠送Maven依赖信息文件:jetty-client-...
jetty-security-9.4.8.v20171121.jar,jetty-io-9.4.8.v20171121.jar,jetty-continuation-9.4.8.v20171121.jar,jetty-client-9.4.8.v20171121.jar,jetty-jmx-9.4.8.v20171121.jar,jetty-plus-9.4.8.v20171121....
jetty-alpn-client-9.2.26.v20180806.jar jetty-alpn-server-9.2.26.v20180806.jar jetty-annotations-9.2.26.v20180806.jar jetty-cdi-9.2.26.v20180806.jar jetty-client-9.2.26.v20180806.jar jetty-continuation...
Jetty源码分析.pdf
jdk1.7 jetty9.2.13 maven3.3.3 很基础的例子,来自官网,写成了DEMO,更加方便学习。包括jetty作为Http 服务器和 Web 容器的基础实现。
jetty服务器性能调整过程分析jetty服务器性能调整过程分析jetty服务器性能调整过程分析jetty服务器性能调整过程分析
花了两天整理的jetty的源代码分析的草稿,初学jetty的可以下来看看,懂jetty的就不要看了
这个包里面包含jetty的http包,最新版的(至今)jetty-7.0,里面根本没有http相关的包,我在进行junit in action学习中,有一个实例JettySample及相关jetty的例子,都必须用到.后来网络上费了很大的劲才找到,很不容易!
使用jetty实现websocket功能,jetty已经集成了websocket的标准方法。
赠送jar包:jetty-http-9.4.11.v20180605.jar; 赠送原API文档:jetty-http-9.4.11.v20180605-javadoc.jar; 赠送源代码:jetty-http-9.4.11.v20180605-sources.jar; 赠送Maven依赖信息文件:jetty-...
从官网下载的JETTY官方jar包 版本包含 1、jetty-distribution-9.4.39.v20210325,官网更新日期为2021.03.25 2、jetty-home-10.0.2 3、jetty-home-11.0.2 后缀为.tar.gz的为linux版本,后缀为.zip的为win版本。
jetty9资源通过官网下载实在太慢,为了国内程序员快速获得jetty资源,特别上jetty在idea编译器上的配置教程。
Jetty9 配置使用HTTPS证书,访问你的服务器更安全,更好的配置方法。
赠送jar包:jetty-http-9.3.19.v20170502.jar; 赠送原API文档:jetty-http-9.3.19.v20170502-javadoc.jar; 赠送源代码:jetty-http-9.3.19.v20170502-sources.jar; 赠送Maven依赖信息文件:jetty-...
NULL 博文链接:https://vista-rui.iteye.com/blog/1386427
jetty-http-7.4.2.v20110526.jar jetty-http 服务jar包