设为首页 收藏本站
查看: 994|回复: 0

[经验分享] jetty http client 实现分析

[复制链接]

尚未签到

发表于 2017-2-26 11:42:16 | 显示全部楼层 |阅读模式
背景


谈到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 threadshttpClient.setTimeout(5000); // 5 seconds timeout; if no server reply, the request expirehttpClient.start();



  • 运行
               ContentExchange exchange = new ContentExchange(true) {@Overrideprotected void onResponseComplete() throws IOException {if (getResponseStatus() == 200) {String content = getResponseContent();System.out.println(content);}}@Overrideprotected 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,但无论如何比起常规的连接池模式强了不少

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-347413-1-1.html 上篇帖子: maven+jetty+eclipse实现调试 下篇帖子: 为jetty配置SSL
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表