了解版本为主要版本为1.5.12。主要从四个方面了解:主循环逻辑、TCP连接处理流程、HTTP连接处理流程。

主循环处理流程主循环

的主循环在.c中的()函数,代码如下:

/* Runs the polling loop */
void run_poll_loop()
{
    int next;
    tv_update_date(0,1);
    while (1)
    {
        /* check if we caught some signals and process them */
        signal_process_queue();
        /* Check if we can expire some tasks */
        wake_expired_tasks(&next);
        /* Process a few tasks */
        process_runnable_tasks(&next);
        /* stop when there's nothing left to do */
        if (jobs == 0)
            break;
        /* The poller will ensure it returns around  */
        cur_poller.poll(&cur_poller, next);
        fd_process_cached_events();
    }
}

主循环的结构比较清晰,就是循环的调用几个函数,并在适当的时候结束循环并退出:

处理信号队列。超时任务。处理可运行的任务。检测是否可以结束循环。执行 poll 处理 fd 的 IO 事件。处理可能仍有 IO 事件的 fd.- 处理信号队对列

实现了自己的信号处理机制。接受到信号之后,将该信号放到信号队列中。在程序运行到()时处理所有位于信号队列中的信号。

– 唤醒超时任务

的顶层处理逻辑是task,task上存储着要处理的任务的全部信息。task的管理是采用队列方式,同时分为和。顾名思义,是需要等待一定时间的task的集合,而则代表需要立即执行的task的集合。

该函数就是检查中那些超时的任务,并将其放到中。在执行的过程中,会因为一些情况导致需要将当前的任务通过调用等接口放到中。

ks- 处理可运行的任务

处理位于中的任务。

前面提到,可能将一些超时的任务放到中。此外,执行的过程中,还有可能通过调用直接讲某个task放到中,这代表程序希望该任务下次尽可能快的被执行。

对于TCP或者HTTP业务流量的处理,该函数最终通过调用来完成,包括解析已经接收到的数据, 并执行一系列load 的特性,但不负责从收发数据。

jobs == 0- 无任务可执行,结束循环

中用jobs记录当前要处理的任务总数,一个也会被计算在内。因此, 如果jobs为0的话,通常意味着要退出了,因为连都要释放了。 jobs的数值通常在时更新。因此,是否可以退出循环,就放在了所有任务的执行之后。

.poll()- 执行poll处理fd的IO事件

启动阶段,会检测当前系统可以启用那种异步处理的机制,比如、poll、epoll、等,并注册对应的poll方法。epoll的相关函数接口在.c中。

这里就是执行已经注册的的poll方法,主要功能就是获取所有活动的fd,并调用对应的,完成接受新建连接、数据收发等功能。

处理可能仍有IO事件的fd

的poll方法执行时,程序会将某些符合条件以便再次执行IO处理的的fd放到 list[]中,ents()函数会再次执行这些fd的io 。

TCP连接处理流程关键数据结构

负责处理请求的核心数据结构是 ,本文不对该数据结构进行分析。

从业务的处理的角度,简单介绍一下对的理解:

此外,一个,通常还要对应一个task,最终用来做调度的是通过task.

相关初始化

在正式处理请求之前,会有一系列初始化动作。这里介绍和请求处理相关的一些初始化。

初始化处理TCP连接的方法

初始化处理TCP协议的相关数据结构,主要是和相关的方法的声明。详细见下面 (.c)的初始化:

/* Note: must not be declared  as its list will be overwritten */
static struct protocol proto_tcpv4 = {
    .name = "tcpv4",
    .sock_domain = AF_INET,
    .sock_type = SOCK_STREAM,
    .sock_prot = IPPROTO_TCP,
    .sock_family = AF_INET,
    .sock_addrlen = sizeof(struct sockaddr_in),
    .l3_addrlen = 32/8,
    .accept = &listener_accept,
    .connect = tcp_connect_server,
    .bind = tcp_bind_listener,
    .bind_all = tcp_bind_listeners,
    .unbind_all = unbind_all_listeners,
    .enable_all = enable_all_listeners,
    .get_src = tcp_get_src,
    .get_dst = tcp_get_dst,
    .drain = tcp_drain,
    .pause = tcp_pause_listener,
    .listeners = LIST_HEAD_INIT(proto_tcpv4.listeners),
    .nb_listeners = 0,
};

初始化

,顾名思义,就是用于负责处理监听相关的逻辑。

在解析bind配置的时候赋值给的proto成员。函数调用流程如下:

cfgparse.c
-> cfg_parse_listen
-> str2listener
-> tcpv4_add_listener
-> listener->proto = &proto_tcpv4;

由于这里初始化的是处理的一些方法。可以推断,接收新建连接的入口函数应该是结构体中的方法。对于tcpv4来说,就是()函数。

的其他初始化

cfgparse.c
-> check_config_validity
-> listener->accept = session_accept;
   listener->frontend = curproxy; (解析 frontend 时,会执行赋值: curproxy->accept = frontend_accept)
   listener->handler = process_session;

整个配置文件解析完毕,也已初始化完毕。可以简单梳理一下几个方法的设计逻辑:

下文分析TCP新建连接处理过程,基本上就是这三个函数的分析。

绑定所有已注册协议上的

haproxy.c
-> protocol_bind_all
-> all registered protocol bind_all
-> tcp_bind_listeners (TCP)
-> tcp_bind_listener
-> [ fdtab[fd].iocb = listener->proto->accept ]

该函数指针指向结构体的成员,即函数。

启用所有已注册协议上的

把所有 的 fd 加到 lists 中 .c -> -> all -> (TCP) -> 函数会将处于 的 的状态修改为 ,并调用 cur 的 set 方法, 比如使用 ,就会调用 .

TCP 连接的处理流程接受新建连接

前面几个方面的分析,主要是为了搞清楚当请求到来时,处理过程中实际的函数调用关系。以下分析TCP建连过程。

haproxy.c
-> run_poll_loop
-> cur_poller.poll
-> _do_poll (如果配置使用的是 sepoll,则调用 ev_sepoll.c 中的 poll 方法)
-> fd_process_polled_events
-> fdtab[fd].iocb (TCP 协议的该函数指针指向 listener_accept )
-> listener_accept
-> 按照 global.tune.maxaccept 的设置尽量可能多执行系统调用 accept,然后再调用 l->accept(),即 listener 的 accept 方法 session_accept
-> session_accept

主要完成以下功能:

初始化s->txn。p->执行proxy的方法即。TCP连接上的接收事件

haproxy.c
-> run_poll_loop
-> cur_poller.poll
-> _do_poll (如果配置使用的是 sepoll,则调用 ev_sepoll.c 中的 poll 方法)
-> fd_process_polled_events
-> fdtab[fd].iocb(fd) (该函数在建连阶段被初始化为四层协议的 read 方法,对于 TCP 协议,为 conn_fd_handler)
-> conn_fd_handler
-> conn->data->recv(对应si_conn_recv_cb)
-> si_conn_recv_cb

主要完成以下功能:

唤醒任务,把当前任务加入到run queue中。随后检测 tasks时,就会处理该任务。TCP连接上的发送事件

haproxy.c
-> run_poll_loop
-> cur_poller.poll
-> _do_poll (如果配置使用的是 sepoll,则调用 ev_sepoll.c 中的 poll 方法)
-> fdtab[fd].iocb(fd) (该函数在建连阶段被初始化为四层协议的 write 方法,对于 TCP 协议,为 conn_fd_handler)
-> conn_fd_handler
-> conn->data->send(对应si_conn_send_cb)
-> si_conn_send_cb

主要完成以下功能:

———END———
限 时 特 惠: 本站每日持续更新海量各大内部创业教程,永久会员只需109元,全站资源免费下载 点击查看详情
站 长 微 信: nanadh666

声明:1、本内容转载于网络,版权归原作者所有!2、本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。3、本内容若侵犯到你的版权利益,请联系我们,会尽快给予删除处理!