Chinaunix首页 | 论坛 | 博客
  • 博客访问: 17462
  • 博文数量: 7
  • 博客积分: 15
  • 博客等级: 民兵
  • 技术积分: 30
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-10 10:17
文章分类
文章存档

2014年(2)

2013年(5)

我的朋友

分类: LINUX

2014-02-24 14:24:57

在nginx listen配置项的说明中有一个选项:

deferred -- indicates to use that postponed accept(2) on Linux with

. the aid of option TCP_DEFER_ACCEPT. 我们知道通常影响服务端性能的四大原因有:内存拷贝、内存分配、系统调用,进程切换。而nginx的deffered选项的作用是用来降低服务端进行epoll_ctl、epoll_wait(linux下)的次数(系统调用)和降低服务端保持的连接句柄数,从而提高服务端处理性能的设置。设置这个选项以后只有客户端有数据到达时才被epoll_wait监听到,再去accept和处理连接数据。

点击(此处)折叠或打开

  1. void
  2. ngx_event_accept(ngx_event_t *ev)
  3. {
  4.     ......
  5.         if (ev->deferred_accept) {
  6.             rev->ready = 1;
  7. #if (NGX_HAVE_KQUEUE)
  8.             rev->available = 1;
  9. #endif
  10.         }
  11.         ls->handler(c);
  12.     ......
  13. }
而ls->handler在ngx_http_add_listening中被初始化为 ls->handler = ngx_http_init_connection,那么继续看

点击(此处)折叠或打开

  1. void
  2. ngx_http_init_connection(ngx_connection_t *c)
  3. {
  4.     ......
  5.     if (rev->ready) {
  6.         /* the deferred accept(), rtsig, aio, iocp */
  7.         if (ngx_use_accept_mutex) {
  8.             ngx_post_event(rev, &ngx_posted_events);
  9.             return;
  10.         }
  11.     ......
  12. }
在ngx_http_init_connection中就将接收到的事件放到posted队列中等待线程的处理。而在为设置deferred 选项时,这个操作是要我们在将接收到的连接epoll_ctl(ADD)之后再epoll_wait探测到EPOLLIN事件时才做这个处理的,从这里看我们就减少了一次epoll_wait该事件的过程,而且等到有数据时才accept,那么和未设置deffered选项时相比减少了accept到有数据之间这段事件服务器维护的一条连接。
但是这个过程时如何实现的呢?
我们知道TCP建立连接时进行三次握手协议,即客户端发送sync,服务端sync、ack,客户端ack即连接建立,这个时候服务端就对这个连接句柄进行accept。而查查TCP_DEFER_ACCEPT这个选项,可以了解到在tcp_check_req中有如下代码:

点击(此处)折叠或打开

  1. struct sock *tcp_check_req(struct sock *sk,struct sk_buff *skb,
  2.              struct request_sock *req,
  3.              struct request_sock **prev)
  4. {
  5.        ......
  6.         /* If TCP_DEFER_ACCEPT is set, drop bare ACK. */
  7.         if (inet_csk(sk)->icsk_accept_queue.rskq_defer_accept &&
  8.          TCP_SKB_CB(skb)->end_seq == tcp_rsk(req)->rcv_isn + 1) {//如果选项设置了,并且是裸
  9.                                                                    ack,丢弃该ack;选项值得默
  10.                                                                    认为1
  11.             inet_rsk(req)->acked = 1;
  12.             return NULL;
  13.         }
  14.         child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb,
  15.                                  req, NULL);//如果非裸ack或没设置选项则建立连接(req从半连接
  16.                                               队列到连接队列及tcp状态变为ESTABLISHED)
  17.         ......
  18. }

TCP在处理链接请求时,服务器发送sync,ack后,处理客户端来的数据时,如果客户端只是对服务端的sync的ack那么则丢弃对这个裸ack,而直到有数据一起到来时再将tcp状态转换为ESTABLISHED,服务器端才对这个监听的句柄进行accept处理。

当然在服务端丢弃裸ack后就破坏了tcp的三次握手协议的过程,针对这个内核使用的是一个 超时重传sync、ack的机制,其以一个2的等比数列的方式进行重传。这部分具体的处理,请参考内核tcp协议栈的实现部分。

阅读(2021) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~