实现异步请求池,以DNS服务器为例【池式组件】

异步请求池用来干嘛

  • 统一申请一部分请求
  • 将请求从同步变成异步

比如业务上需要DNS请求、mysql请求、redis请求等等,可以将这些请求统一进行管理和将同步变为异步。

异步请求池怎么实现

  • epoll管理IO
  • 响应的回调函数使用线程

异步请求池的API

  • commit
  • init
  • destroy
  • callback
struct epoll_arg{//epoll参数

	async_result_cb cb;
	int fd;
};
struct async_context{
	int epfd;
	pthread_t thid;
};

init

  1. 创建epoll
  2. 创建线程并传入请求的回调函数
int dns_async_context_init(struct async_context *ctx){

	if(ctx == NULL)return -1;

	int epfd = epoll_create(1);
	if(epfd<0)return NULL;

	ctx->epfd = epfd;

	int ret = pthread_create(&ctx->epfd, NULL, dns_async_callback, ctx);//将ctx作为参数传给回调函数
	//在回调函数中,从参数获得对应epfd,然后再进行epoll_wait
	if(ret<0){
		return NULL;
		close(epfd);
	}

	return 0;
}

commit

  1. 先进行DNS请求
  2. 再将请求的fd放入epfd
int dns_async_client_commit(struct async_context *ctx, async_result_cb cb){
	
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (sockfd < 0) {
		perror("create socket failed\n");
		exit(-1);
	}

	printf("url:%s\n", domain);

	struct sockaddr_in dest;
	bzero(&dest, sizeof(dest));
	dest.sin_family = AF_INET;
	dest.sin_port = htons(53);
	dest.sin_addr.s_addr = inet_addr(DNS_SVR);
	
	int ret = connect(sockfd, (struct sockaddr*)&dest, sizeof(dest));
	printf("connect :%d\n", ret);

	struct dns_header header = {0};
	dns_create_header(&header);

	struct dns_question question = {0};
	dns_create_question(&question, domain);

	char request[1024] = {0};
	int req_len = dns_build_request(&header, &question, request);
	int slen = sendto(sockfd, request, req_len, 0, (struct sockaddr*)&dest, sizeof(struct sockaddr));

	struct epoll_arg *eparg = (struct epoll_arg*)calloc(1, sizeof(struct epoll_arg));
	if(eparg==NULL)return -1;

	eparg->fd = sockfd;
	eparg->cb = cb;
		
	struct epoll_event ev;
	ev.events = EPOLLIN;
	ev.data.ptr = eparg; 
	
	epoll_ctl(ctx->epfd, EPOLL_CTL_ADD, sockfd, &ev);
		
	//请求之后加入epoll就退出
	return 0;
}

callback

  1. 线程获取arg参数并进行epoll_wait
  2. 等到了有事件发生就进行处理并将其从epfd中移除出来
  3. 最后释放DNS资源
void *dns_async_callback(void *arg){

	struct async_context *ctx = (struct async_context*)arg;
	
	while(1){	
		struct epoll_event events[ASYNC_CLIENT_NUM] = {0};

		int nready = epoll_wait(ctx->epfd, events, ASYNC_CLIENT_NUM, -1);
		if(nready<0) continue;

		int i = 0;
		for(i=0; i<nready; i++){

			struct epoll_arg *data = event[i].data.ptr;
			int sockfd = data->fd;

			char buffer[1024] = {0};
			struct sockaddr_in addr;
			
			size_t addr_len = sizeof(struct sockaddr_in);

			int n = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&addr, (socklen_t*)&addr_len);
				
			printf("recvfrom n : %d\n", n);
			struct dns_item *domains = NULL;
			int count = dns_parse_response(buffer, &domains);

			data->cb(domains, count);
			
			epoll_ctl(ctx->epfd, EPOLL_CTL_DEL, sockfd, &ev);

			close(sockfd);
			
			dns_async_free_domain(domains, count);
			free(data);
		}
		
	}
}

destroy

  • 释放epoll、线程资源

总结

  • 异步请求池如何提升效率?
    异步请求池在io上并非是异步,而是一个请求完成之后将这个请求放入epoll,不进行等待对方响应就进行下一个下一个请求。异步的实现最主要是使用线程实现。主线程请求,回调线程处理数据,将请求与处理响应分开,而不是请求完等待响应再请求下一个,提升了效率。
  • 异步请求池和协程的区别?
    首先说协程是如何实现异步的,协程是保留了各个协程和调度器切换前的工作环境,当切换时再切换回去。而异步请求池是使用线程来实现异步。请求之后交由其他线程去响应。而协程是全部过程都在同一个线程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fallsom

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值