异步请求实现

9 篇文章 0 订阅
4 篇文章 0 订阅

什么是异步请求

redis/mysql提供的客户端,hiredis/mysqlclient都是同步的。request发出后,就在等着response。

mysql_query(sql), 一个请求发出后,当前线程挂起等待,当mysql server返回结果后,函数才返回。这就是一个同步的请求过程。

异步请求,就是请求发出后,不需要等待结果,函数直接返回,当server返回结果后,调用回调函数进行处理。

如何做一个异步请求?

  1. 发送请求,是一个连接还是多个连接?

指的是一个请求没有返回,再发起一个请求,是用原来的连接还是新建连接?

使用新的连接。也就是多个连接。

  1. 使用epoll,对fd进行管理。

  2. 请求与响应的数据,不能做到一个线程。新开一个线程,去等待结果的返回。

核心思路就是request发出后,将fd加入到epoll,在另一个线程中利用epoll对多个fd进行检测,当fd可读的时候,调用回调函数进行处理。

如何设计?

封装一组api

  1. init --> 异步操作上下文

1)epoll_create

2)pthread_create,创建线程,使用epoll

  1. commit

1)建立网络连接

2)组织好对应的协议 redis/mysql

3)发送数据到对应的服务器

4)将fd加入到epoll,检测fd是否可读。

  1. callback

1)epoll_wait(), 检测哪些fd可读。

2)recv();

3)读出协议数据,并进行解析,处理, 调用commit中的cb;

4)将fd从epoll中delete掉

  1. destroy

1)close(epfd)

2)pthread_cancel

异步请求返回的response,怎么跟之前的request对应起来呢?

一个io发送完请求后,收到server的回复后,才会再发送下一次请求,所以request和reply是一一对应的。

request发出,没有response返回,怎么处理?

使用定时器,超时处理,timefd

"池"体现在哪里?

fd存储在epoll里面,是多个fd,即多个连接,在一个线程中同时监控多个fd。

代码实现

init

int  dns_async_context_init(struct async_context  *ctx) {

	if (ctx == NULL) return -1;

	// epoll_create

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

	ctx->epfd = epfd;
	

	// pthread_create

	int ret = pthread_create(&ctx->thid, NULL, dns_async_callback, ctx);
	if (ret) {
		close(epfd);
		return -1;
	}

	return 0;
}

commit

int dns_async_client_commit(struct async_context *ctx, async_result_cb cb) {
	// socket
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (sockfd < 0) {
		perror("create socket failed\n");
		exit(-1);
	}

	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);
	//1 connect server
	
	int ret = connect(sockfd, (struct sockaddr*)&dest, sizeof(dest));

	// encode protocol
	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);

	// send 
	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.data.ptr = eparg;
	ev.events = EPOLLIN;
	epoll_ctl(ctx->epfd, EPOLL_CTL_ADD, sockfd, &ev);

#endif
	return 0;
}

callback

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 = events[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, NULL);

			close(sockfd);
			

			dns_async_free_domain(domains);
			free(data);

		}

	}


}

destroy

int dns_async_context_destroy() {

	// close(epfd)
	

	// pthread_cancel(thid)
	

	free();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值