在阅读本文之前,需要熟悉DNS协议,请参考:https://blog.csdn.net/xianjian1990/article/details/104182896

文章目录

  • 同步与异步的区别
  • 为什么要做异步请求池
  • 如何做异步请求池
  • 代码实现DNS异步请求池

同步与异步的区别

要设计异步请求池,首先要明白什么是异步、同步。

异步:就是发送完消息,不用等待结果的返回。发送消息的线程 和 处理消息的线程 是并行的
同步:就是在发送消息后要等待返回结果,返回结果没有回来的时候这个线程是等待(阻塞)的状态,发送消息的线程 和 处理消息的线程 是串行的

从上面的概念可以得知做成异步的好处是:
1、不需要等待(阻塞)返回结果,可以尽快的处理其他任务。
2、发送消息的线程 和 处理消息的线程 是并行的,这样可以减少一次发送到接收结果的程序运行时间。

为什么要做异步请求池

当业务服务器访问需要等待的服务时,业务访问线程需要等待挂起直到该服务给出反馈,例如对mysql,redis,http,dns等服务的请求,这些请求的返回需要等待一段时间,大大加深了业务服务器的承载压力,也会影响其总体性能,异步请求池就是为解决这个问题应运而生的。

如何做异步请求池


如上图,请求池是属于请求端(客户端)的,请求端 发送连接 给 被请求端,被请求端处理完成后发送结果给请求端。异步请求的话,在发送完连接后,请求端的这个发送线程就可以处理其他任务,无需等待结果。

现在是异步请求池,说明是要建立多个连接。然后都**不需要等待结果的返回。**当结果有返回的时候,请求端在找到对应的发连接的信息,把结果转发给它。
也就是说,当建立多个连接,就会有多个fd,然后,把这些发送完消息的fd,存到epoll中进行管理。当被请求端发送结果过来的时候,epoll可以找到对应的fd,进行处理。 ( 需要注意的是调用 建立连接函数 的是一个线程,处理接收到结果消息的是一个线程。 )

异步请求池的实现组成4元组:1.提交commit,2.线程回调函数,3.init 4.释放
工作流程:

  1. init
    epoll创建用来监听fd变化;
    线程创建用来接受返回数据;

  2. 提交commit
    创建socket,建立连接
    封装协议
    调用发送数据到对应的第三方服务器
    发送完成后,将发送的fd设置为可读事件添加到epoll管理

  3. 线程回调函数
    epoll_wait()检查哪些fd可读
    遍历可读fd,recv接收数据
    读出的数据按照对应的协议进行解析并进行操作

  4. 释放销毁
    对应fd关闭close
    线程退出释放

代码实现DNS异步请求池

  • 初始化init
    初始化函数dns_async_client_init,不需要接收参数。其中主要是创建 epoll的fd,使用此fd来管理 commit (连接)的 fd,还有要创建处理结果的线程,线程函数是dns_async_client_proc。
struct async_context {int epfd;
};
//dns_async_client_init()
//epoll init
//thread init
/*struct async_context *ctx = dns_async_client_init();*/
struct async_context *dns_async_client_init(void) {int epfd = epoll_create(1); // if (epfd < 0) return NULL;struct async_context *ctx = calloc(1, sizeof(struct async_context));if (ctx == NULL) {close(epfd);return NULL;}ctx->epfd = epfd;pthread_t thread_id;int ret = pthread_create(&thread_id, NULL, dns_async_client_proc, ctx);if (ret) {perror("pthread_create");return NULL;}usleep(1); //child go firstreturn ctx;
}
  • 提交commit

建立连接函数是dns_async_client_commit,接收参数:struct async_context* ctx、 const char* domain、async_result_cb cb。

首先说一下domain,这个是请求的url。cb是针对此函数中要创建的fd的接收到的结果处理的回调函数。ctx表示async_context结构体指针,此结构中存的是epoll的fd。在此函数还有很多dns的代码,这里就不进行介绍了,百度上应该有很多的。


//dns_async_client_commit(ctx, domain)
//socket init
//dns_request
//sendto dns send
/*dns_async_client_commit(ctx, domain[i], dns_async_client_result_callback);*/
int dns_async_client_commit(struct async_context* ctx, const char *domain, 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);set_block(sockfd, 0); //nonblockstruct 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 ep_arg *eparg = (struct ep_arg*)calloc(1, sizeof(struct ep_arg));if (eparg == NULL) return -1;eparg->sockfd = sockfd;eparg->cb = cb;struct epoll_event ev;ev.data.ptr = eparg;ev.events = EPOLLIN;ret = epoll_ctl(ctx->epfd, EPOLL_CTL_ADD, sockfd, &ev); //printf(" epoll_ctl ADD: sockfd->%d, ret:%d\n", sockfd, ret);return ret;
}
  • 线程回调函数
    dns_async_client_proc函数是处理被请求端返回结果的,是处理所有fd的是否接收到消息的函数,此函数中会一直循环处理epoll_wait,判断是不是有fd来消息了。
    epoll_wait循环的判断epoll的fd对应的红黑树中是不是有client的fd来消息了,epoll_wait函数中最后的参数-1表示的是阻塞等待 如果有fd来消息了会返回有多少个fd来消息了,并且把这个些fd从红黑树中移动到一个链表中。nready表示有多少个fd来了消息。
typedef void (*async_result_cb)(struct dns_item *list, int count);
/*typedef void (*async_result_cb)(struct dns_item *list, int count);*/
/*dns_async_client_commit(ctx, domain[i], dns_async_client_result_callback);*/
static void dns_async_client_result_callback(struct dns_item *list, int count) {int i = 0;for (i = 0;i < count;i ++) {printf("name:%s, ip:%s\n", list[i].domain, list[i].ip);}
}
//dns_async_client_proc()
//epoll_wait
//result callback
/*int ret = pthread_create(&thread_id, NULL, dns_async_client_proc, ctx);*/
/*接受请求的监听*/
static void* dns_async_client_proc(void *arg) {struct async_context *ctx = (struct async_context*)arg;int epfd = ctx->epfd;while (1) {struct epoll_event events[ASYNC_CLIENT_NUM] = {0};int nready = epoll_wait(epfd, events, ASYNC_CLIENT_NUM, -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 ++) {struct ep_arg *data = (struct ep_arg*)events[i].data.ptr;int sockfd = data->sockfd;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);struct dns_item *domain_list = NULL;int count = dns_parse_response(buffer, &domain_list);data->cb(domain_list, count); //call cbint ret = epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);//printf("epoll_ctl DEL --> sockfd:%d\n", sockfd);close(sockfd); /dns_async_client_free_domains(domain_list, count);free(data);}}}
  • 销毁释放
int dns_async_clinet_destroy( struct async_context * ctx )
{close(ctx->epfd);
pthread_cancel(ctx->threadId);
return 0;
}

推荐一个零声学院免费公开课程,个人觉得老师讲得不错,
分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,
fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,
TCP/IP,协程,DPDK等技术内容,点击立即学习:服务器课程

DNS异步请求池原理与实现相关推荐

  1. httpservletrequest 设置请求头_大部分程序员不知道的 Servelt3 异步请求,原来这么简单?

    前言 当一个 HTTP 请求到达 Tomcat,Tomcat 将会从线程池中取出线程,然后按照如下流程处理请求: 将请求信息解析为 HttpServletRequest 分发到具体 Servlet 处 ...

  2. 内存泄漏的3个解决方案与原理实现,掌握一个轻松应对开发丨内存池|mtrace||API实现|框架封装|中间件|异步请求|连接池

    内存泄漏的3个解决方案与原理实现,掌握一个轻松应对开发 视频讲解如下,点击观看: 内存泄漏的3个解决方案与原理实现,掌握一个轻松应对开发丨内存池|mtrace||API实现|框架封装|中间件|异步请求 ...

  3. 【OkHttp】OkHttp 源码分析 ( 同步 / 异步 Request 请求执行原理分析 )

    OkHttp 系列文章目录 [OkHttp]OkHttp 简介 ( OkHttp 框架特性 | Http 版本简介 ) [OkHttp]Android 项目导入 OkHttp ( 配置依赖 | 配置 ...

  4. SpringMvc异步请求的使用及部分原理

    最近隔壁项目组的项目又出问题了,一直被用户投诉太卡了,页面白屏的那种,打开源代码一看,全是非异步请求,类似于以下写法: @ResponseBody@RequestMapping(value = &qu ...

  5. python线程池原理_Django异步任务线程池实现原理

    这篇文章主要介绍了Django异步任务线程池实现原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 当数据库数据量很大时(百万级),许多批量数据修改 ...

  6. java异步线程池同时请求多个接口数据

    java异步线程池同时请求多个接口数据 一.适合的使用场景 复杂的网页爬虫,如要同时请求多个不同网页的数据,并且需要执行不同的数据处理,这个是非常合适的,执行线程传递的参数到最后callback是会附 ...

  7. Ajax异步请求原理和过程

    Ajax异步请求原理和过程 1.什么是Ajax 2.AJAX创建异步对象XMLHttpRequest ( 考虑兼容性 ) 3.操作XMLHttpRequest 对象 1.什么是Ajax Ajax是一种 ...

  8. Ajax异步请求原理

    一.Ajax能做什么 异步请求.局部刷新 二.同步请求 JavaScript的特点是单线程,也正是因为单线程造成了同步请求 1. 为什么JS是单线程: JS 作为游览器脚本语言,主要用途是和用户交互. ...

  9. 异步请求-XMLHttpRequest、ajax、axios原理浅析

    异步请求-XMLHttpRequest.ajax.axios原理浅析 XMLHTTPRequest 参考 readyState responseType XMLHttpRequest Level 1 ...

最新文章

  1. 十进制转化为十六进制分割高低位
  2. 机器学习中的特征工程总结!
  3. CSS background-position随笔
  4. python读文本文件的过程是怎样的_读写文本文件的步骤_Python读写txt文本文件的操作方法全解析...
  5. NumPy之:ndarray中的函数
  6. Sphinx编译docs文档
  7. 传记 | 我的大学三年-不以物喜,不以己悲
  8. python学什么方向就业好_学python就业都有哪些方向
  9. Java 学习如逆水行舟,不进则退,100 本 java 电子书推荐
  10. URL与URI的区别和联系
  11. 将word文档锁定其他用户不可编辑
  12. android怎实现拼图功能,基于Android的趣味拼图的实现
  13. STM32F103C8T6最小系统原理图和PCB
  14. 台式计算机 cpu型号大全,台式机CPU参数对照表
  15. 【测试】优秀软件测试工程师必备的8个能力
  16. c语言msgbox函数,msgbox函数的返回值类型为什么
  17. Java实习生常规技术面试题每日十题Java基础(一)
  18. Oracle函数之单值函数
  19. [概率论]图像里的“白噪声”——电视机搜不到台时雪花斑点的形成原因 (不信谣,不传谣,与宇宙微波背景辐射没有任何关系)
  20. 计算机的工作原理是二进制原理吗,计算机是怎么通过二进制原理来工作的?

热门文章

  1. C语言学习笔记-有符号数和无符号数相加的问题
  2. TypeScript实战-04-TS枚举类型
  3. matlab读取excel数据
  4. 程序猿没有副业,太难了(个人经历不喜无视)
  5. 3650m5设置u盘启动_联想启天M415设置u盘启动步骤(支持uefi/bios双启动)
  6. 数据分析-1.数据分析介绍
  7. 【每日早报】2019/08/08
  8. 【Beta】Scrum Meeting 7 与助教谈话
  9. Fintech趣店总部(厦门)技术招聘
  10. 利用MATLAB模拟排队论