zoukankan      html  css  js  c++  java
  • libeasy代码学习:一次简单的TCP请求处理流程

    libeasy是基于libev实现的一套高并发、异步非阻塞的事件库,与libev相比又做了一层封装,更加方便处理各种网络请求。目前已经被阿里多个对性能有较高要求的核心软件采用(数据库,动态、静态数据cache等),我写的log_pipe的网络部分同样也是采用libeasy的,不过跟核心就没有关系了,哈哈。今天我要写的是一次最简单的网络请求事件要在libeasy中经历的流程。后续会介绍涉及到磁盘io等需要异步响应的流程。

    1、easy_eio_create
      初始化线程池的各个线程相应的数据结构,并对listen_watcher添加一个Timer事件,每0.1秒触发一次,对应的回调函数是easy_connection_on_listen。easy_connection_on_listen定时起来去抢listen的锁。
      如果抢到锁,则把当前的listen线程改成自己,并添加这个线程的EV_READ事件,回调函数同样是easy_connection_on_accept;
      如果抢不到锁,则会停止自己对listen的EV_READ事件的监听,即在接下来的0.1秒,所有的accept读事件都由抢到锁的这个线程来触发。

    2、easy_connection_add_listen
      打开监听的端口,把线程池的每个线程数据结构都加上对这个listenfd(位于easy_eio_t结构的listen结构题)的EV_READ事件,其中的回调函数设置为easy_connection_on_accept。这意味着在开始阶段,线程池的每个线程都会响应accept事件。

    3、easy_connection_on_accept
      由线程池的某个线程触发,对listenfd做accept操作,成功后会创建easy_connection_t对象;并且注册对这个fd的读写超时事件easy_connection_on_(readable/writable/timeout_conn)。正常情况下该请求后续所有的事件都会注册在这个线程的ev_loop上,除非达到设置的负载条件,会停掉该链接在这个线程的事件,并把这个线程的所有事件注册到另一个线程ev_loop上。
      调用easy_handler_t->on_connect回调(第1个我们自己要实现的回调函数,非必需
      调用easy_switch_listen,这个函数会把自己的listen_watcher重新设置为0.5秒,然后重新启动这个timer,并且释放listen的锁;这就意味此时其它线程可以通过easy_connection_on_listen来获得listen锁了,而且因为其它线程都是0.1秒,而当前线程设置的是0.5秒,这样就等于把listen的机会让给了其它线程,有利于线程之间的负载均衡。刚开始的时候我误以为当前线程的time是easy_connection_on_listen函数中设置的60秒,实际这个60秒的作用只是相当于停掉当前线程的timer,因为当自己获得listen锁期间是不需要这个timer的。

    4、easy_connection_on_readable
      当socket有数据到来时,会触发读事件的回调函数,它首先会创建一个easy_message_t,一个decode单位对应一个easy_message_t。所以一个easy_message_t到底包含一个或多个request或者其他什么东西,是取决于我们自己的实现的。
      数据全部读到easy_message_t后会有判断,如果是当前是server端会调用easy_connection_do_request,如果是客户端会调用easy_connection_do_response。我今天讲的的是服务器端,所以我们接下来看easy_connection_do_request

    5、easy_connection_do_request
      恩,数据已经读完了,连傻姑都猜到下面该解析数据了,哈哈。此时会调用easy_connnect_t->easy_handler_t->decode(第2个我们自己要实现的回调函数,一般都需要实现这个函数),decode可能会调用若干次,知道把easy_message_t中接收的buffer解析完。对于每一次decode调用,创建一个easy_request_t对象r,并把r加到request列表,把decode返回的packet挂到r->ipacket上面,然后调用easy_connection_process_request

    6、easy_connection_process_request
      调用easy_connect_t->easy_handle_t->process(第3个我们自己要实现的回调函数,一般都需要实现),对一个请求单位(我之所以是一个请求单位,是因为这里并不一定是一个完整的请求,准确的说应该是decode出来的一个buf单位)做处理。在process中,process有两种正常的返回值:EASY_OK表示我们已经知道要响应什么数据,或者不需要响应client。EASY_AGAIN表示目前我们还没有准备好响应数据。下一篇文章我们将介绍EASY_AGAIN的情况。下面介绍EASY_OK时候的情况。
      调用easy_connection_request_done:如果存在r->opacket,调用easy_connect_t->easy_handle_t->encode(第4个我们自己要实现的回调函数,一般都需要实现,除非客户端不需要响应),并且设置此次请求对应的cleanup方法
      调用easy_connection_write_socket,把r->output数据通过socket写出去

    至此,一次最简单的tcp请求处理流程就走完了。

      

      

  • 相关阅读:
    一个简单的knockout.js 和easyui的绑定
    knockoutjs + easyui.treegrid 可编辑的自定义绑定插件
    Knockout自定义绑定my97datepicker
    去除小数后多余的0
    Windows Azure Web Site (15) 取消Azure Web Site默认的IIS ARR
    Azure ARM (1) UI初探
    Azure Redis Cache (3) 创建和使用P级别的Redis Cache
    Windows Azure HandBook (7) 基于Azure Web App的企业官网改造
    Windows Azure Storage (23) 计算Azure VHD实际使用容量
    Windows Azure Virtual Network (11) 创建VNet-to-VNet的连接
  • 原文地址:https://www.cnblogs.com/jinhuafeng/p/libeasy1.html
Copyright © 2011-2022 走看看