Redis的实现四:事件循环和计时器

       我们的服务器缺少了一个内容超时。每个网络应用程序都需要处理超时,因为网络的另一边可能会消失。不要只进行持续的IO操作,如读/写需要超时,但启动空闲的TCP连接也是一个好主意。要实现超时,必须修改事件循环,因为轮询是唯一被阻塞的东西。

我们的代码如下:

int rv=poll(poll_args.data(),(nfds_t)poll_args.size(),1000);

         poll系统调用接受一个timeout参数,该参数规定了用于poll系统调用的时间上限。超时值目前是1000毫秒的任意值。如果我们根据计时器设置超时值,poll应该在过期时唤醒,或在此之前唤醒;然后我们就有机会在适当的时候启动计时器。

        问题是我们可能有多个计时器,poll的超时值应该是最近的计时器的超时值。需要一些数据结构来查找最近的计时器。堆数据结构是查找最小/最大值的常用选择,通常用于此目的。此外,还可以使用任何用于排序的数据结构。例如,我们可以使用AVL树来排序计时器,并可能扩展树来跟踪最小值。

        让我们从添加计时器来踢出空闲的TCP连接开始。对于每个连接都有一个计时器,设置为一个固定的超时,每次在连接上有IO活动时,计时器都会更新为一个固定的超时。注意,当我们更新计时器时,它变成了最遥远的一个;因此,我们可以利用这一事实来简化数据结构;一个简单的链表足以保持计时器的顺序:新的或更新的计时器只是到列表的末尾,列表保持有序的顺序。同样,链表上的操作是O(1),哪个比排序数据结构更好

定义链表是一个微不足道的任务:

struct DList {DList *prev = NULL;DList *next = NULL;
};
inline void dlist_init(DList *node) {node->prev = node->next = node;
}
inline bool dlist_empty(DList *node) {return node->next == node;
}
inline void dlist_detach(DList *node) {DList *prev = node->prev;DList *next = node->next;prev->next = next;next->prev = prev;
}
inline void dlist_insert_before(DList *target, DList *rookie) {DList *prev = target->prev;prev->next = rookie;rookie->prev = prev;rookie->next = target;target->prev = rookie;
}

get_monotonic_usec是获取时间的函数。注意时间戳必须是单调的。时间戳向后跳转会给计算机系统带来各种各样的麻烦。

static uint64_t get_monotonic_usec() {timespec tv = {0, 0};clock_gettime(CLOCK_MONOTONIC, &tv);return uint64_t(tv.tv_sec) * 1000000 + tv.tv_nsec / 1000;
}

下一步是将列表添加到服务器和连接结构中。

static struct {HMap db;// a map of all client connections, keyed by fdstd::vector<Conn *> fd2conn;// timers for idle connectionsDList idle_list;
} g_data;
struct Conn {int fd = -1;uint32_t state = 0; // either STATE_REQ or STATE_RES// buffer for readingsize_t rbuf_size = 0;uint8_t rbuf[4 + k_max_msg];// buffer for writingsize_t wbuf_size = 0;size_t wbuf_sent = 0;uint8_t wbuf[4 + k_max_msg];uint64_t idle_start = 0;// timerDList idle_list;
};

修改后的事件循环概述:

int main() {// some initializationsdlist_init(&g_data.idle_list);int fd = socket(AF_INET, SOCK_STREAM, 0);// bind, listen & other miscs// code omitted...// the event loopstd::vector<struct pollfd> poll_args;while (true) {// prepare the arguments of the poll()// code omitted...// poll for active fdsint timeout_ms = (int)next_timer_ms();int rv = poll(poll_args.data(), (nfds_t)poll_args.size(), timeout_ms);if (rv < 0) {die("poll");}// process active connections
for (size_t i = 1; i < poll_args.size(); ++i) {if (poll_args[i].revents) {Conn *conn = g_data.fd2conn[poll_args[i].fd];connection_io(conn);if (conn->state == STATE_END) {// client closed normally, or something bad happened.// destroy this connectionconn_done(conn);}}
}// handle timersprocess_timers();// try to accept a new connection if the listening fd is active
if (poll_args[0].revents) {
(void)accept_new_conn(fd);}}return 0;
}

修改了几件事:
1. poll的超时参数由next_timer_ms函数计算。
2. 销毁连接的代码被移到了conn_done函数中。
3. 添加了用于触发计时器的process_timers函数。
4. 计时器在connection_io中更新,在accept_new_conn中初始化。
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/628066.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

单元测试:Testing leads to failure, and failure leads to understanding

单元测试的概念可能多数读者都有接触过。作为开发人员&#xff0c;我们编写一个个测试用例&#xff0c;测试框架发现这些测试用例&#xff0c;将它们组装成测试 suite 并运行&#xff0c;收集测试报告&#xff0c;并且提供测试基础设施&#xff08;断言、mock、setup 和 teardo…

JAVAEE初阶 文件IO(一)

这里写目录标题 一. 计算机中存储数据的设备1.1 CPU1.2 内存1.3 硬盘1.4 三种存储的区别 二.文件系统2.1 相对路径2.2 绝对路径2.3 .和..的含义2.4 例子2.5 everything工具 三.文件3.1 文本文件3.2 二进制文件 四. JAVA对于文件的API4.1 getParent getName getPath getAbsolute…

Jest单元测试:玩转代码的小捉迷藏!

Jest Jest 是什么&#xff1f; Jest 是一个流行的 JavaScript 测试框架&#xff0c;专注于简化和改进代码的测试流程。它由 Facebook 开发并维护&#xff0c;具有以下特点&#xff1a; 1、易用性&#xff1a;Jest 提供了一个简单而强大的测试框架&#xff0c;使得编写和运行测…

【架构设计】单体软件向微服务化演变

单体软件 假设单体软件的各模块如下&#xff0c;其中服务包含许多功能模块&#xff0c;如用户管理模块、商品模块、订单模块、仓库模块; #mermaid-svg-MzWKwMCwfo3PWMGH {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-…

uniapp h5 发行后 微信第二次打开网址 页面白屏

发行后把网址给客户&#xff0c;第一次可以正常登录打开&#xff0c;第二次打开白屏 原因&#xff1a;第一次打开时没有token&#xff0c;所以跳转登录页&#xff0c;可以正常访问 第二次打开时有token&#xff0c;但是网址根目录没有配置默认页面&#xff0c;所以白屏 解决…

Windows Server调整策略实现999999个远程用户用时登录

正文共&#xff1a;1234 字 23 图&#xff0c;预估阅读时间&#xff1a;2 分钟 上篇文章中&#xff08;Windows Server 2019配置多用户远程桌面登录服务器&#xff09;&#xff0c;我们主要介绍了Windows Server 2019在配置远程桌面时&#xff0c;如何通过3种方式创建本地用户账…

微前端无界机制浅析

简介 随着项目的发展&#xff0c;前端SPA应用的规模不断加大、业务代码耦合、编译慢&#xff0c;导致日常的维护难度日益增加。同时前端技术的发展迅猛&#xff0c;导致功能扩展吃力&#xff0c;重构成本高&#xff0c;稳定性低。 为了能够将前端模块解耦&#xff0c;通过相关技…

使用Qt连接scrcpy-server控制手机

Qt连接scrcpy-server 测试环境如何启动scrcpy-server1. 连接设备2. 推送scrcpy-server到手机上3. 建立Adb隧道连接4. 启动服务5. 关闭服务 使用QTcpServer与scrcpy-server建立连接建立连接并视频推流完整流程1. 开启视频推流过程2. 关闭视频推流过程 视频流的解码1. 数据包协议…

python每日学17:控制推导逻辑的子表达式不要超过两个

背景&#xff1a;今天放假在家&#xff0c;《python学习手册》不在身边&#xff0c;所以今天学习《Effective Python: 编写高质量Python代码的90个有效方法》第28条《控制推导逻辑的子表达式不要超过两个》&#xff0c;这本书已经是第二版了&#xff0c;第一版是《编写高质量py…

shell脚本,ADB

Linux命令行命令是系统内置的命令或用户自定义的脚本&#xff08;shell 脚本&#xff0c;.sh扩展名结尾&#xff09;&#xff0c;可以通过终端输入命令来执行。这些命令通常存储在Linux系统的/bin、/usr/bin、/sbin、/usr/sbin等目录下&#xff0c;也可以在$PATH环境变量中指定…

NVMe系统内存结构 - Meta Data

NVMe系统内存结构 - Meta Data 1 为什么需要数据保护2 Meta Data定义3 Meta Data传输方式4 常见Meta Data使用场景4.1 不带数据保护信息4.2 带数据保护信息“数据写”流程4.3 带数据保护信息“数据读”流程4.4 SSD内部加入数据保护信息4.5 SSD内部根据数据保护信息验证数据 本文…

如何在你的网站接入QQ登录?

文章目录 准备阶段申请QQ登录的权限创建应用最后上传qqlogin.php代码 准备阶段 国内服务器和备案域名需要你有张独一无二本人的身份证你正面手持身份证的图片一张100px*100px的网站图标 申请QQ登录的权限 首先访问qq互联&#xff0c;点击我直接访问 登陆完成后我们点击面的…

bash shell基础命令(一)

1.shell启动 shell提供了对Linux系统的交互式访问&#xff0c;通常在用户登录终端时启动。系统启动的shell程序取决于用户账户的配置。 /etc/passwd/文件包含了所有用户的基本信息配置&#xff0c; $ cat /etc/passwd root:x:0:0:root:/root:/bin/bash ...例如上述root账户信…

Python新年文字烟花简单代码

简单的Python新年烟花代码示例&#xff1a; import random import timedef create_firework():colors [红色, 橙色, 黄色, 绿色, 蓝色, 紫色]flashes [爆裂, 闪光, 旋转, 流星, 喷射]color random.choice(colors)flash random.choice(flashes)print(f"发射一枚{color…

redis之单线程和多线程

目录 1、redis的发展史 2、redis为什么选择单线程&#xff1f; 3、主线程和Io线程是怎么协作完成请求处理的&#xff1f; 4、IO多路复用 5、开启redis多线程 1、redis的发展史 Redis4.0之前是用的单线程&#xff0c;4.0以后逐渐支持多线程 Redis4.0之前一直采用单线程的主…

GUI编程(函数解析以及使用)

1.介绍 AWT&#xff08;Abstract Window Toolkit&#xff09;和Swing 是 Java 提供的用于创建图形用户界面&#xff08;GUI&#xff09;的类库。 AWT&#xff1a;AWT 是 Java 最早提供的 GUI 类库&#xff0c;它基于本地平台的窗口系统&#xff0c;使用操作系统的原生组件进行…

17.蒙特卡洛强化学习之批量式策略评估

文章目录 1. 是什么2. 为什么直接估计Q而不是V2. 根据多条完整轨迹计算Q(s,a)3. 初访法和每访法哪个更好&#xff1f; 1. 是什么 智能体利用完整轨迹数据估计出 Q π ( s , a ) Q_\pi(s,a) Qπ​(s,a)的过程 2. 为什么直接估计Q而不是V 因为策略评估的目的在于改进现有策略 …

Springboot中时间格式化

时间格式化方式 JsonFromat方式全局配置方式格式化工具方式 JsonFromat方式 前端传参或后端响应 yyyy-MM-dd HH:mm:ss 格式&#xff0c;直接属性字段上加注解 JsonFromat JsonFromat(pattern "yyyy-MM-dd HH:mm:ss", timezone "GMT8") private Date fi…

文件的创建时间可以修改吗,怎么改?

文件的创建时间可以修改吗&#xff0c;怎么改&#xff1f;文件的创建时间是由操作系统自动生成并记录的&#xff0c;通常情况下无法直接修改。创建时间是文件的属性之一&#xff0c;它反映了文件在文件系统中的生成时间。一旦文件被创建&#xff0c;其创建时间就被确定下来&…

pod配置资源管理

secret 保存密码&#xff0c;token&#xff0c;敏感的k8s资源 configmap(重点) 1.2加入的新特征 这类数据可以存放在镜像当中&#xff0c;但是放在secret当中可以更方便的控制&#xff0c;减少暴露的风险&#xff0c;保存加密的信息 secret的类型 docker-registry 存储docke…