博客园  :: 首页  :: 新随笔  :: 管理

2.3.1 异步请求与协程

Posted on 2023-03-15 06:16  wsg_blog  阅读(34)  评论(0编辑  收藏  举报

Linux C/C++服务器

异步请求与协程

1.1 什么是异步?

什么是异步?什么是同步?
同步:为一发一收,发完之后阻塞等待回传数据,处理完成然后再发下一条
异步:一直发,commit提交请求接口会一直连续提交请求,有一个专门的等待回传数据的线程,这里我们用epoll来管理接收数据的fd

1.2 DNS请求示例

以DNS为例,演示下同步和异步性能对比,分别向dns服务器发送45个网址,解析返回对应的ip信息,查看用时

  • 同步用时1194ms
  • 异步用时54ms

    异步请求池应用:我们自己的服务器与mysql服务器、redis服务器、grpc等进行数据收发时会显著提升服务器性能

1.3 多线程实现异步请求池

发送和接收要在不同的线程中进行,接收数据要用epoll来管理,通过fd来确认接收的数据是哪个请求的回传数据

  • 创建数据接收线程
  • 创建管理fd的epoll

1.3.1 主要的接口组成

  1. commit();
  2. thread_callback(); epoll_wait();
  3. init_ctx(); epoll_create(); pthread_create();
  4. destory_ctx(); pthread_cancel(threadid);close(fd);
    代码就是示例中的async_dns_client_noblock.c,可以把dns协议换成http,就是异步的http请求

1.3.2 实现思路

  • dns_async_client_init()接口:epoll_create(1)创建epfd,pthread_create()创建接收回传数据线程
  • dns_async_client_proc()接口:epoll_wait()等待回传数据事件,就绪后会recv,接受完close(fd)
  • dns_async_client_commit()接口:socket()创建sockfd,把dns协议转为requset数据发送包,connect()连接dns服务器,sendto()发送请求,epoll_ctl(ctx->epfd, EPOLL_CTL_ADD, sockfd, &ev)将创建的sockfd添加到epoll中
  • main函数中for循环发送45个请求,getchar()防止程序结束

2.1 协程实现异步(单线程)

协程:同步的编程方式,异步的性能;怎么理解? 其实协程干了上边接收线程干的活,也有人称协程为轻量级的线程,那它是怎么干这个活的呢?主旨思想其实就是jump跳转
在千万级io或者更高的量级上使用的较多

协程异步(替换接收线程)思路:

  1. 检测sockfd是否就绪
  2. if就绪,执行recv_from()
  3. if没有就绪,跳转,跳转至commit代码段,发送新的请求
//伪代码
int async_recv_from(int sockfd){
    epoll_create();
    while(1){
        int nready=epoll_wait(epfd, events, EVENTS_LENGTH, 0);  //非阻塞
        if(events[i].events & EPOLLIN){
            recv_form();
        }else{
            epoll_ctl(epdf, sockfd...);
            
            //跳转至commit()
            longjmp();
        }
    }
}
int commit(){
    for(){
        send();  
    }
}

协程实际使用是比较复杂的,需要实现协程调度器,类似于线程调度器,感兴趣可以去找下开源的代码