企业级请求池实现

1 前言

请求池出现的运用场景,主要是为了解决同步处理带来的阻塞导致效率的大大降低,异步请求池将请求事件和接收事件结果解析过程放到不同线程,大大的减少了中间阻塞等待的时间,从而提升事件处理效率,在多业务处理情况下,具有极其重要的意义。

2 异步请求池原理

2.1 基本原理

异步请求池的原理,主要是利用两个线程将发送请求和接收服务器回应两个事件分开,这样可以减少中间耗时操作的等待。具体操作是发送完请求后将soketfd加入到epoll队列,接收线程在epoll_wait中等待epoll队列的事件,接收到fd事件后,读取响应数据分发到具体的业务处理函数处理。
在这里插入图片描述

2.2 结构体设计

上下文定义主要包括:发送对象,发送数据,epoll对象和线程id;发送数据包括,数据和长度;
接收数据包括,soketfd和响应回调函数。

//响应回调函数
typedef void (*result_callback)(void *data, int len);
//发送数据
typedef struct rp_send_data {
	unsigned char *data;
	int len;
}rp_send_data;
//接收数据
typedef struct rp_recv_data {
	int sfd;
	result_callback re_cb;
}rp_recv_data;

//上下文
typedef struct rp_context {

	int epfd;
	pthread_t thid;
	struct sockaddr_in dst;
	rp_send_data sdata;
}rp_context;

3 请求池实现

3.1 请求池创建

请求池创建,主要工作包括创建epoll对象和请求池的接收响应线程,并将数据句柄放到上下文中。

int rp_pool_create(rp_context *ctx)
{
	if(ctx == NULL) return -1;

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

	ctx->epfd = epfd;
	
	pthread_t thread_id;
	int ret = pthread_create(&thread_id, NULL, rp_pool_response, ctx);
	if(ret)
	{
		close(epfd);
		printf("phtead_create failed!\n");
		ctx->thid = 0;
		return -3;
	}

	ctx->thid = thread_id;
	
	return 0;
}

3.2 请求池销毁

请求池释放,需要释放epoll对象和响应线程。

int rp_pool_destory(rp_context *ctx)
{
	if(ctx->thid)
		pthread_cancel(ctx->thid);

	if(ctx->epfd)
		close(ctx->epfd);
	
	return 0;
}

3.3 请求响应函数

请求响应函数是响应线程的主函数,利用epoll_wait等待对方的请求响应事件,接收到数据后,利用回调函数进行事件的处理。

void *rp_pool_response(void *arg)
{
	rp_context *ctx = (rp_context *)arg;
	if(ctx == NULL) return NULL;

	printf("epoll epfd:%d\n", ctx->epfd);
	
	while(1)
	{
		struct epoll_event ev[EPOLL_EVENTS_MAX];
		int nready = epoll_wait(ctx->epfd, ev, EPOLL_EVENTS_MAX, -1);
		if(nready < 0)
		{
			if (errno == EINTR || errno == EAGAIN) {
				continue;
			} else {
				break;
			}
		} else if (nready == 0) {
			continue;
		}
		
		printf("nready:%d\n", nready);
		int i = 0; 
		for(i = 0; i < nready; i++)
		{
			if(ev[i].events == EPOLLIN)
			{
				rp_recv_data *arg = (rp_recv_data *)ev[i].data.ptr;

				struct sockaddr_in src;
				size_t sock_len = sizeof(struct sockaddr_in);
				
				int sockfd = 0;
				unsigned char buf[RECV_BUFFER_LEN] = {0};
				int len = 0;
				
				sockfd = arg->sfd;
				len = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&src, (socklen_t*)&sock_len);
				if(len < 0)
				{
					perror("recvfrom:");
					continue;
				}
//				printf("recv len:%d\n", len);
				arg->re_cb(buf, len);

				int ret = epoll_ctl(ctx->epfd, EPOLL_CTL_DEL, sockfd, NULL);
				//printf("epoll_ctl DEL --> sockfd:%d\n", sockfd);

				close(sockfd);

				free(arg);
			}
		}
	}

	return NULL;
}

3.3 请求函数

请求函数是需要根据不同的应用场景进行,可能发生在不同的业务中,主要功能是创建非阻塞的soket对象,并将事先处理好的数据发送到目标服务器上,需要注意的是发送后要将soketfd加入到epoll队列管理。

int rp_pool_commit(rp_context *ctx)
{
	//socket create
	int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
	if(socket_fd < 0)
	{
		perror("socket:");
		return -1;
	}

	set_block(socket_fd, 0); //nonblock

	int ret = connect(socket_fd, (struct sockaddr *)&ctx->dst, sizeof(struct sockaddr_in));
	if(ret) 
	{
		perror("connect:");
		return -1;
	}
#if 0
	char ip[64] = {0};
	inet_ntop(AF_INET , &ctx->dst.sin_addr , ip , sizeof(struct sockaddr));
	printf("dst ip:%s\n", ip);
#endif
	//send 
//	printf("sendto data:%p,len:%d\n", ctx->sdata.data, ctx->sdata.len);
	ret = sendto(socket_fd, ctx->sdata.data, ctx->sdata.len, 0, (struct sockaddr *)&ctx->dst, sizeof(struct sockaddr_in));
	if(ret < 0) 
	{
		perror("sendto:");
		return -2;
	}

	rp_recv_data *rd = (rp_recv_data *)malloc(sizeof(rp_recv_data));
	if(rd == NULL) return -1;
	
	rd->sfd = socket_fd;
	rd->re_cb = dns_result_response;

	//epoll add
	struct epoll_event ev;
	ev.data.ptr = rd;
	ev.events = EPOLLIN;
	ret = epoll_ctl(ctx->epfd, EPOLL_CTL_ADD, socket_fd, &ev);
//	printf(" epoll_ctl ADD: sockfd->%d, ret:%d\n", socket_fd, ret);
	if(ret) return -4;
	
	return 0;
}

具体实现移步:myreqpool.c

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值