当前位置: 首页 > news >正文

惊群 java_(转)测试Lighttpd accept的惊群现象

lighttpd里面采用的是prefork的模型,在fork进程之前就已经创建好了listen socket

那么fork了进程池之后,所有进程都有一份自己独立的listen socket fd,

但实际上这个独立的fd 对应的确是一个文件表项,即实际上任然是一个共享的文件描述符

在阻塞模型中,各进程分别通过accept阻塞,等待连接到达,当一个连接到达时,所有的进程都会被唤醒,但只有其中一个进程可以成功accept该连接,其余的则继续投入睡眠,这就是所谓的惊群现象

lighttpd使用的是非阻塞IO复用模型,测试一下是否会有惊群现象呢?

先把结论给出:

1.比如有20个进程注册了listen socket的请求连接事件,当一个连接到达确实会有多个进程 被通知有事件要处理(但不是全部,大约只有5,6个进程)

2.被唤醒的这几个进程会调用accept函数,其中只有一个成功返回连接fd,其余进程均返回EAGAIN或者 EWOULDBLOCK错误(因为是非阻塞的)

测试方法,自己写了一个prefork进程 + epoll的非阻塞server,启动20个进程,client telnet,打印服务器日志

try to accept new connection,pid=29879

try to accept new connection,pid=29876

try to accept new connection,pid=29880

process 29879 accept connection

accept EAGAIN error pid=29876

try to accept new connection,pid=29875

accept EAGAIN error pid=29880

accept EAGAIN error pid=29875

四个进程被通知有事件处理,1个成功accept,3个返回EAGAIN

在lighttpd中,server当被通知有连接要处理时,server会通过循环执行

accept,直到返回错误,或者超过一个上限值

这样,当海量请求连接到达时,似乎惊群不会带来太多的性能损耗。

[cpp]

enum conn_states {

conn_listening, /** the socket which listens for connections */

conn_read, /** reading in a command line */

conn_write, /** writing out a simple response */

conn_nread, /** reading in a fixed number of bytes */

conn_swallow, /** swallowing unnecessary bytes w/o storing */

conn_closing, /** closing this connection */

conn_mwrite, /** writing out many items sequentially */

};

typedef struct{

int fd;

int state;

}conn;

[/cpp]

[cpp]

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include "event.h"

#include "base.h"

//forward declaration

static fdevents *ev;

static conn **conns;

static int freetotal;

static int freecurr;

static int create_listen_fd(char *addr,int port);

static int conn_init();

static conn *get_conn_from_freelist();

static int add_conn_to_freelist(conn *c);

conn *conn_new(int fd,int state);

static int conn_init(){

freetotal=200;

freecurr=0;

conns=(conn **)malloc(freetotal * sizeof(*conns));

if(!conns){

return -1;

}

return 0;

}

static conn *get_conn_from_freelist(){

conn *con;

if(freecurr > 0){

con=conns[–freecurr];

conns[freecurr]=NULL;

return con;

}

return NULL;

}

static int add_conn_to_freelist(conn *c){

if(freecurr

conns[freecurr++]=c;

return 0;

}else{

conn **new_conns=(conn **)realloc(conns,sizeof(*new_conns)*2*freetotal);

if(new_conns){

freetotal*=2;

conns=new_conns;

conns[freecurr++]=c;

return 0;

}

}

return -1;

}

conn *conn_new(int fd,int state){

conn *c;

c=get_conn_from_freelist();

if(!c){

c=(conn *)malloc(sizeof(*c));

}

c->fd=fd;

c->state=state;

return c;

}

static int create_listen_fd(char *addr,int port){

int fd,val,flags;

struct sockaddr_in sockaddr;

fd=socket(AF_INET,SOCK_STREAM,0);

if(fd==-1){

fprintf(stderr,"socket()\n");

return -1;

}

val=1;

if(setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val))<0){

fprintf(stderr,"reuseaddr\n");

return -1;

}

if((flags=fcntl(fd,F_GETFL,0)<0) || fcntl(fd,F_SETFL,flags | O_NONBLOCK) < 0){

fprintf(stderr,"nonblocking\n");

return -1;

}

bzero(&sockaddr,sizeof(sockaddr));

sockaddr.sin_family=AF_INET;

sockaddr.sin_port=htons(port);

inet_pton(AF_INET,addr,&sockaddr.sin_addr);

if(bind(fd,(struct sockaddr *)&sockaddr,sizeof(sockaddr))<0){

fprintf(stderr,"bind error %s",strerror(errno));

return -1;

}

if(listen(fd,2048)<0){

fprintf(stderr,"listen %s",strerror(errno));

return -1;

}

return fd;

}

void event_handler(int fd,void *ctx, int revents){

struct sockaddr_in addr;

socklen_t sock_len;

int done=0,connfd;

conn *c;

c=(conn *)ctx;

while(!done){

switch(c->state){

case conn_listening:

printf("try to accept new connection,pid=%d\n",getpid());

sock_len=sizeof(addr);

connfd=accept(fd,(struct sockaddr *)&addr,&sock_len);

if(connfd>0){

printf("process %d accept connection\n",getpid());

c = conn_new(connfd,conn_read);

fdevent_register(ev,connfd,event_handler,c);

fdevent_event_add(ev,connfd,FDEVENT_IN);

}else{

if(errno== EAGAIN || errno == EWOULDBLOCK){

printf("accept EAGAIN error pid=%d\n",getpid());

}

if(errno==EINTR){

printf("accept EINTR error pid=%d\n",getpid());

}

if(errno==ECONNABORTED){ /* this is a FreeBSD thingy */

printf("accept EABORTED error pid=%d\n",getpid());

}

if(errno==EMFILE){

printf("accept EMFILE error pid=%d\n",getpid());

}

}

done=1;

break;

case conn_read:

printf("on read");

break;

}

}

}

int main(int argc,char **argv){

int fd,o;

char *listen_addr;

int port,num_childs,max_fds;

struct rlimit rlim;

conn *c;

port=0;

num_childs=5;

while(-1!=(o=getopt(argc,argv,"l:p:f:h"))){

switch(o){

case ‘l’:

listen_addr=strdup(optarg);

break;

case ‘p’:

port=atoi(optarg);

break;

case ‘f’:

num_childs=atoi(optarg);

break;

case ‘h’:

printf("Usage -l listen addr\n");

printf("Usage -p listen port \n");

printf("Usage -f fork num\n");

exit(1);

}

}

if(!listen_addr){

listen_addr=strdup("127.0.0.1");

}

if(!port){

printf("port is unknown\n");

exit(1);

}

if(0 != getrlimit(RLIMIT_NOFILE,&rlim)){

fprintf(stderr,"getrlimit failed.reason %s\n",strerror(errno));

exit(1);

}

max_fds=rlim.rlim_cur;

//create listen socket

if(-1==(fd=create_listen_fd(listen_addr,port))){

fprintf(stderr,"create listen fd failed\n");

exit(1);

}

//prefork child

if(num_childs > 0){

int child=0;

while(!child){

if(num_childs >0){

switch(fork()){

case -1:

return -1;

case 0:

child=1;

break;

default:

num_childs–;

break;

}

}else{

int status;

if(-1 !=wait(&status)){

num_childs++;

}else{

//ignore

}

}

}

}

//child process event

conn_init();

c=conn_new(fd,conn_listening);

ev=fdevent_init(max_fds);

if(!ev){

fprintf(stderr,"fdevent_init()\n");

exit(1);

}

fdevent_register(ev,fd,event_handler,c);

fdevent_event_add(ev,fd,FDEVENT_IN);

fdevent_poll(ev,1000);

}

[/cpp]

相关文章:

  • Python——私有化 和 属性property
  • python again_收藏!最全从Python小白到大牛,要走的路这里都有(初级篇)
  • .Net Core缓存组件(MemoryCache)源码解析
  • php 函数变量 前加,php在函数和变量前面加上@和$符号的区别详解
  • 凸函数与简森不等式(Jensen's inequality)
  • php date参数n,总结PHP date()参数列表
  • 小程序自定义函数—数字千位转换
  • tp3.2.3php环境要求,TP3.2.3开发手册
  • 控件模板
  • php 热点,PHP+jQuery实现中国地图热点数据统计展示效果
  • phpspy.php,一款php后门 phpspy的情况
  • Appium 之处理首次启动手机App时的系统权限弹框
  • 体对角线 matlab,Matlab计算结果显示求不出精确解
  • 数据库ACID操作---事务四原则
  • php html转pdf 简书,Laravel Html 导出 PDF 方案 ----- wkhtmltopdf laravel-snappy
  • github从入门到放弃(1)
  • socket.io+express实现聊天室的思考(三)
  • 配置 PM2 实现代码自动发布
  • 前端 CSS : 5# 纯 CSS 实现24小时超市
  • 如何在 Tornado 中实现 Middleware
  • 项目管理碎碎念系列之一:干系人管理
  • 由插件封装引出的一丢丢思考
  • nb
  • elasticsearch-head插件安装
  • ​【C语言】长篇详解,字符系列篇3-----strstr,strtok,strerror字符串函数的使用【图文详解​】
  • ​Base64转换成图片,android studio build乱码,找不到okio.ByteString接腾讯人脸识别
  • #if 1...#endif
  • #QT(TCP网络编程-服务端)
  • $redis-setphp_redis Set命令,php操作Redis Set函数介绍
  • $refs 、$nextTic、动态组件、name的使用
  • (10)Linux冯诺依曼结构操作系统的再次理解
  • (32位汇编 五)mov/add/sub/and/or/xor/not
  • (42)STM32——LCD显示屏实验笔记
  • (env: Windows,mp,1.06.2308310; lib: 3.2.4) uniapp微信小程序
  • (删)Java线程同步实现一:synchronzied和wait()/notify()
  • .NET Core WebAPI中封装Swagger配置
  • .net on S60 ---- Net60 1.1发布 支持VS2008以及新的特性
  • .NET单元测试
  • .NET开源项目介绍及资源推荐:数据持久层
  • [ai笔记4] 将AI工具场景化,应用于生活和工作
  • [Angular] 笔记 21:@ViewChild
  • [C++] Windows中字符串函数的种类
  • [C++]指针与结构体
  • [Codeforces] combinatorics (R1600) Part.2
  • [EULAR文摘] 脊柱放射学持续进展是否显著影响关节功能
  • [HDOJ4911]Inversion
  • [HTML]Web前端开发技术18(HTML5、CSS3、JavaScript )HTML5 基础与CSS3 应用——喵喵画网页
  • [Java安全入门]三.CC1链
  • [Lua实战]整理Lua中忽略的问题
  • [NOIP2018 PJ T4]对称二叉树
  • [Paper]Application of deep convolutional neural network for automated detection of myocardial...
  • [PostgreSQL的 SPI_接口函数]
  • [redis] Jedis 与 ShardedJedis 设计
  • [SoapUI] SoapUI学习视频地址
  • [ThinkPHP]Arr返回1