异步请求池以及协程基本了解的笔记
这一部分只是稍微听了一耳朵,根据观察校招面试里很少提及,先战略性跳过。
背景
通常我们的server和各种服务(如redis、MYSQL、DNS)进行通信的时候总要经过如下步骤:
- 发起请求
等待–sleep(); 阻塞过程 - 接收响应
这里是同步的代码实现,等待对于业务非常不利,所以我们要引入异步,去掉中间的等待过程
我们目的是实现网络io的异步,即发一个请求,不等待结果,再发后续的请求,有响应的时候在返回结果。
改成异步的方法
核心思路提交(commit)和响应放在单独的线程中,需要如下的函数
-
commit()
-
socket()
-
connect()—>udp为什么要connect?答:udp发connect()是为了探测链路,之后sendto就可以走这条链路
-
准备dns protocol
-
send to()
-
同步方案
recvfrom()
异步方案(读写在不同线程)
epoll_ctl(add)
-
-
thread_callback()
-
init_ctx()
-
- epoll_create(), epoll管理多个io
- pthread_create()
-
destroy_ctx()
创建上下文结构体
struct async_context {
int epfd;
pthread_t thid;
}
初始化上下文环境
有两种形式做法
//参数明确,返回值明确【推荐】
int dns_async_client_init(struct async_context *ctx);
//这种形式代表里面有malloc和init操作
struct async_context *dns_async_client_init(void);
int dns_async_client_init(struct async_context *ctx){
if(ctx == NULL) return EINVAL;
ctx->epfd = epoll_create(1);
//第四个参数是回调函数的参数
//返回0表示成功
int ret = pthread_create(&ctx->thid, NULL, dns_async_client_callback, ctx);
if(ret) {
perror("pthread_create");
return -1;
}
return 0;
}
释放上下文环境
int dns_async_client_destroy(struct async_context *ctx){
if(ctx == NULL) return EINVAL;
pthread_cancel(ctx->thid); //终止线程执行
close(ctx->epfd);
return 0;
}
我们可以观察到异步虽然能够提高性能,但是异步的代码逻辑流程不清晰,且相比于同步更难以理解。
协程
学术上定义是**:轻量级的线程**
一个明显特点:同步的编程方式,异步的性能
在一个函数内部 commit,发送请求,等待结果。也就是在commit的流程中尾部跳转到线程回调函数中对到来事件处理的for循环中。
实现一个asyn_recv_from
来替代recvfrom
,具体来说有以下功能:
- 检查sockfd是否就绪
就绪,执行recv_from
没就绪,进行下一条提交,跳转到epoll_wait()
伪代码如下:
void* callback() {
FLAG:
epoll_wait();
}
int async_recv_from(int sockfd){
if(poll(sockfd, POLLIN)){
recv_from();
}else {
epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd);
longjmp FLAG; //跳转到callback中的epoll_wait()处
return ;
}
}
为什么要有协程?
- 逻辑容易理解。
- 轻量级的线程来使得维护业务更加方便
协程所需要的东西
1 入口函数
2 调度器
3 栈
4 协程本身的recv_from与系统的recv结合
5 多核的问题(兼容性)
杂项
异步实现
- 代码上面
- 开了一个线程
协程设计原理
协程调度器实现与性能测试