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请求处理流程就走完了。

      

      

  • 相关阅读:
    WINDOWS API ——GETFILETIME——获取文件时间
    lua 源码分析之线程对象lua_State
    GPL、BSD、MIT、Mozilla、Apache、LGPL开源协议介绍
    BOOST 线程完全攻略
    宏定义中#和##符号的使用和宏定义展开问题
    weak_ptr<T>智能指针
    轻松记住大端小端的含义(附对大端和小端的解释)
    关于VC预定义常量_WIN32,WIN32,_WIN64
    VC 预定义宏
    php技术之路
  • 原文地址:https://www.cnblogs.com/jinhuafeng/p/libeasy1.html
Copyright © 2011-2022 走看看