PHP头条
热点:

​二、使用异步复用IO使用 毫秒级超时)

异步IO执行流程:

1.首先将标志位设为Non-blocking模式,准备在非阻塞模式下调用connect函数

2.调用connect,正常情况下,因为TCP三次握手需要一些时间;而非阻塞调用只要不能立即完成就会返回错误,所以这里会返回EINPROGRESS,表示在建立连接但还没有完成。

3.在读套接口描述符集(fd_set rset)和写套接口描述符集(fd_set wset)中将当前套接口置位用FD_ZERO()、FD_SET()宏),并设置好超时时间(struct timeval *timeout)

4.调用select( socket, &rset, &wset, NULL, timeout )

返回0表示connect超时,如果你设置的超时时间大于75秒就没有必要这样做了,因为内核中对connect有超时限制就是75秒。

//select 实现毫秒级超时示例:

  1. static void conn_select() {  
  2.     // Open TCP Socket  
  3.     m_Socket = socket(PF_INET,SOCK_STREAM,0);  
  4.     if( m_Socket < 0 )  
  5.     {  
  6.         m_connectionStatus = STATUS_CLOSED;  
  7.         return ERR_NET_SOCKET;  
  8.     }  
  9.     struct sockaddr_in addr;  
  10.     inet_aton(m_Host.c_str(), &addr.sin_addr);  
  11.     addr.sin_port = htons(m_Port);  
  12.     addr.sin_family = PF_INET;  
  13.    
  14.     // Set timeout values for socket  
  15.     struct timeval timeouts;  
  16.    timeouts.tv_sec = SOCKET_TIMEOUT_SEC ;   // const -> 5  
  17.    timeouts.tv_usec = SOCKET_TIMEOUT_USEC ; // const -> 0  
  18.     uint8_t optlen = sizeof(timeouts);  
  19.     if( setsockopt( m_Socket, SOL_SOCKET, SO_RCVTIMEO,&timeouts,(socklen_t)optlen) < 0 )  
  20.     {  
  21.         m_connectionStatus = STATUS_CLOSED;  
  22.         return ERR_NET_SOCKET;  
  23.     }  
  24.     // Set the Socket to TCP Nodelay ( Send immediatly after a send / write command )  
  25.     int flag_TCP_nodelay = 1;  
  26.     if ( (setsockopt( m_Socket, IPPROTO_TCP, TCP_NODELAY,  
  27.             (char *)&flag_TCP_nodelay, sizeof(flag_TCP_nodelay))) < 0)  
  28.     {  
  29.         m_connectionStatus = STATUS_CLOSED;  
  30.         return ERR_NET_SOCKET;  
  31.     }  
  32.     // Save Socket Flags  
  33.     int opts_blocking = fcntl(m_Socket, F_GETFL);  
  34.     if ( opts_blocking < 0 )  
  35.     {  
  36.         return ERR_NET_SOCKET;  
  37.     }  
  38.     //设置为非阻塞模式  
  39.     int opts_noblocking = (opts_blocking | O_NONBLOCK);  
  40.     // Set Socket to Non-Blocking  
  41.     if (fcntl(m_Socket, F_SETFL, opts_noblocking)<0)  
  42.     {  
  43.         return ERR_NET_SOCKET;  
  44.     }  
  45.     // Connect  
  46.     if ( connect(m_Socket, (struct sockaddr *)&addr, sizeof(addr)) < 0)  
  47.     {  
  48.         // EINPROGRESS always appears on Non Blocking connect  
  49.         if ( errno != EINPROGRESS )  
  50.         {  
  51.             m_connectionStatus = STATUS_CLOSED;  
  52.             return ERR_NET_SOCKET;  
  53.         }  
  54.         // Create a set of sockets for select  
  55.         fd_set socks;  
  56.         FD_ZERO(&socks);  
  57.         FD_SET(m_Socket,&socks);  
  58.         // Wait for connection or timeout  
  59.         int fdcnt = select(m_Socket+1,NULL,&socks,NULL,&timeouts);  
  60.         if ( fdcnt < 0 )  
  61.         {  
  62.             return ERR_NET_SOCKET;  
  63.         }  
  64.         else if ( fdcnt == 0 )  
  65.         {  
  66.             return ERR_TIMEOUT;  
  67.         }  
  68.     }  
  69.     //Set Socket to Blocking again  
  70.     if(fcntl(m_Socket,F_SETFL,opts_blocking)<0)  
  71.     {  
  72.         return ERR_NET_SOCKET;  
  73.     }  
  74.    
  75.     m_connectionStatus = STATUS_OPEN;  
  76.     return 0;  

说明:在超时实现方面,不论是什么脚本语言:PHP、Python、Perl 基本底层都是C&C++的这些实现方式,需要理解这些超时处理,需要一些Linux 编程和网络编程的知识。

延伸阅读:

http://blog.sina.com.cn/s/blog_4462f8560100tvgo.html

http://blog.csdn.net/thimin/article/details/1530839

http://hi.baidu.com/xjtdy888/item/93d9daefcc1d31d1ea34c992

http://blog.csdn.net/byxdaz/article/details/5461142

http://blog.163.com/xychenbaihu@yeah/blog/static/13222965520112163171778/

http://hi.baidu.com/suyupin/item/df10004decb620e91f19bcf5

http://stackoverflow.com/questions/7092633/connect-timeout-with-alarm

http://stackoverflow.com/questions/7089128/linux-tcp-connect-with-select-fails-at-testserver?lq=1

http://cppentry.com/bencandy.php?fid=54&id=1129

总结 】

1. PHP应用层如何设置超时?

PHP在处理超时层次有很多,不同层次,需要前端包容后端超时:

浏览器客户端) -> 接入层 -> Web服务器  -> PHP  -> 后端 (MySQL、Memcached)

就是说,接入层Web服务器层)的超时时间必须大于PHPPHP-FPM)中设置的超时时间,不然后面没处理完,你前面就超时关闭了,这个会很杯具。还有就是PHP的超时时间要大于PHP本身访问后端MySQL、HTTP、Memcached)的超时时间,不然结局同前面。

2. 超时设置原则是什么?

如果是希望永久不超时的代码比如上传,或者定期跑的程序),我仍然建议设置一个超时时间,比如12个小时这样的,主要是为了保证不会永久夯住一个php进程或者后端,导致无法给其他页面提供服务,最终引起所有机器雪崩。

如果是要要求快速响应的程序,建议后端超时设置短一些,比如连接500ms,读1s,写1s,这样的速度,这样能够大幅度减少应用雪崩的问题,不会让服务器负载太高。

3. 自己开发超时访问合适吗?

一般如果不是万不得已,建议用现有很多网络编程框架也好、基础库也好,里面一般都带有超时的实现,比如一些网络IO的lib库,尽量使用它们内置的,自己重复造轮子容易有bug,也不方便维护不过如是是基于学习的目的就当别论了)。

4. 其他建议

超时在所有应用里都是大问题,在开发应用的时候都要考虑到。我见过一些应用超时设置上百秒的,这种性能就委实差了,我举个例子:

比如你php-fpm开了128个php-cgi进程,然后你的超时设置的是32s,那么我们如果后端服务比较差,极端情况下,那么最多每秒能响应的请求是:

128 / 32 = 4个 

你没看错,1秒只能处理4个请求,那服务也太差了!虽然我们可以把php-cgi进程开大,但是内存占用,还有进程之间切换成本也会增加,cpu呀,内存呀都会增加,服务也会不稳定。所以,尽量设置一个合理的超时值,或者督促后端提高性能。


www.phpzy.comtrue/php/2425.htmlTechArticle二、使用异步复用IO使用 毫秒级超时) 异步IO执行流程: 1.首先将标志位设为Non-blocking模式,准备在非阻塞模式下调用connect函数 2.调用connect,正常情况下...

相关文章

相关频道:

PHP之友评论

今天推荐