浅析Redis②:命令处理之epoll实现(中)
写在前面
Redis作为我们日常工作中最常使用的缓存数据库,其重要性不言而喻,作为普通开发者,我们在日常开发中使用Redis,主要聚焦于Redis的基层数据结构的命令使用,很少会有人对Redis的内部实现机制进行了解,对于我而言,也是如此,但一直以来,我对于Redis的内部实现都很好奇,它为什么会如此高效,本系列文章是旨在对Redis源代码分析拆解,通过阅读Redis源代码,了解Redis基础数据结构的实现机制。
关于Redis的源码分析,已经有非常多的大佬写过相关的内容,最为著名的是《Redis设计与实现》,对于Redis源码的分析已经非常出色,本系列文章对于源码拆解时,并不会那么详细,相信大部分读者应该不是从事Redis的二次开发工作,对于源码细节过于深入,会陷入细节的泥潭,这是我在阅读源码时尽量避免的,我尽量做到对大体的脉络进行梳理,讲清楚主干逻辑,细节部分,如果读者有兴趣,可以自行参阅源码或相关资料。
本系列源代码,基于Redis 3.2.6
前言
在上一篇中浅析Redis①:命令处理核心源码分析(上),我们大致了解了Redis客户端命令请求的处理流程,在整个流程中,我们还有两个问题没有解释:
1、非阻塞的核心epoll是如何实现的?
2、Redis是如何将数据写回Client端的?
本篇我们就围绕第一个问题,寻找答案,继续看Redis客户端命令请求的处理流程。
Redis的epoll实现
Redis的非阻塞I/O是指Redis在处理客户端请求时,不会一直等待I/O操作完成,而是会尽快返回,并在I/O操作完成后通知Redis进行后续处理。
epoll作为非阻塞I/O的实现,是Linux内核提供的一种多路I/O复用机制。epoll可以监视多个文件描述符,一旦某个文件描述符就绪,epoll就会通知Redis进行后续处理。
Redis的非阻塞I/O模型可以提高并发处理能力,在阻塞I/O模型中,Redis在处理一个客户端请求时,如果遇到I/O操作,会一直等待I/O操作完成,这意味着Redis无法处理其他客户端的请求。
而在非阻塞I/O模型中,Redis在遇到I/O操作时,会尽快返回,并在I/O操作完成后通知Redis进行后续处理。这样,Redis就可以同时处理多个客户端的请求,提高了并发处理能力。
同时非阻塞I/O模型还可以减少Redis的CPU占用率。在阻塞I/O模型中,Redis在遇到I/O操作时,会一直等待I/O操作完成,这意味着Redis的CPU会一直处于占用状态。
在非阻塞I/O模型中,Redis在遇到I/O操作时,会尽快返回,并在I/O操作完成后通知Redis进行后续处理。这样CPU就不会一直处于占用状态,可以减少CPU占用率,提升CPU使用效率。
核心实现
Redis非阻塞IO的实现是基于OS的内核函数支持,源码逻辑如下:
redis.c中main方法启动,执行initServer()初始化redis配置,同时创建非阻塞事件监听器:
server.el = aeCreateEventLoop(server.maxclients+REDIS_EVENTLOOP_FDSET_INCR);for (j = 0; j < server.ipfd_count; j++) {if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,acceptTcpHandler,NULL) == AE_ERR){redisPanic("Unrecoverable error creating server.ipfd file event.");}
}
其中,ipfd_count默认参数为1024,该参数表示Redis可以同时处理的最大TCP连接数。
aeCreateEventLoop与aeCreateFileEvent的实现逻辑在ae.c文件中:
aeEventLoop *aeCreateEventLoop(int setsize) {aeEventLoop *eventLoop;int i;if ((eventLoop = zmalloc(sizeof(*eventLoop))) == NULL) goto err;eventLoop->events = zmalloc(sizeof(aeFileEvent)*setsize);eventLoop->fired = zmalloc(sizeof(aeFiredEvent)*setsize);if (eventLoop->events == NULL || eventLoop->fired == NULL) goto err;eventLoop->setsize = setsize;eventLoop->lastTime = time(NULL);eventLoop->timeEventHead = NULL;eventLoop->timeEventNextId = 0;eventLoop->stop = 0;eventLoop->maxfd = -1;eventLoop->beforesleep = NULL;if (aeApiCreate(eventLoop) == -1) goto err;/* Events with mask == AE_NONE are not set. So let's initialize the* vector with it. */for (i = 0; i < setsize; i++)eventLoop->events[i].mask = AE_NONE;return eventLoop;err:if (eventLoop) {zfree(eventLoop->events);zfree(eventLoop->fired);zfree(eventLoop);}return NULL;
}int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,aeFileProc *proc, void *clientData)
{if (fd >= eventLoop->setsize) {errno = ERANGE;return AE_ERR;}aeFileEvent *fe = &eventLoop->events[fd];if (aeApiAddEvent(eventLoop, fd, mask) == -1)return AE_ERR;fe->mask |= mask;if (mask & AE_READABLE) fe->rfileProc = proc;if (mask & AE_WRITABLE) fe->wfileProc = proc;fe->clientData = clientData;if (fd > eventLoop->maxfd)eventLoop->maxfd = fd;return AE_OK;
}
其中aeApiCreate()是核心创建逻辑,aeApiCreate()方法采用了类似Java中多态的实现方式,由于C本身并不支持多态,因此需要使用C中的技巧实现:
/* Include the best multiplexing layer supported by this system.* The following should be ordered by performances, descending. */
#ifdef HAVE_EVPORT
#include "ae_evport.c"
#else#ifdef HAVE_EPOLL#include "ae_epoll.c"#else#ifdef HAVE_KQUEUE#include "ae_kqueue.c"#else#include "ae_select.c"#endif#endif
#endif
这段代码是Redis中的一个条件编译语句,用于根据不同的操作系统和编译器选择不同的事件驱动库。
事件驱动库是Redis的一个核心组件,用于处理各种事件,包括网络IO事件、定时器事件等。Redis支持多种事件驱动库,比如epoll、kqueue、select等。在编译Redis时,需要根据操作系统和编译器选择合适的事件驱动库进行编译。
这段代码中,首先判断是否定义了HAVE_EVPORT宏。如果定义了该宏,则使用ae_evport.c文件中的事件驱动库,否则继续判断是否定义了HAVE_EPOLL宏。如果定义了该宏,则使用ae_epoll.c文件中的事件驱动库,否则继续判断是否定义了HAVE_KQUEUE宏。如果定义了该宏,则使用ae_kqueue.c文件中的事件驱动库,否则使用ae_select.c文件中的事件驱动库。
这种条件编译技术可以使Redis在不同操作系统和编译器下具有更好的兼容性和可移植性,使得Redis可以在不同的平台上运行,并且可以充分发挥不同平台的优势。
简言之,就是根据不同的操作系统,决定选择不同的内核IO模型,优先级: evport > epoll > kqueue > select
关于系统内核实现,参考:
#ifdef HAVE_EVPORT: 如果定义了宏 HAVE_EVPORT,则包含文件 ae_evport.c。ae_evport.c 可能包含了 Solaris 10 系统使用的事件驱动库。
#else: 如果没有定义宏 HAVE_EVPORT,则继续处理后续代码。
#ifdef HAVE_EPOLL: 如果定义了宏 HAVE_EPOLL,则包含文件 ae_epoll.c。ae_epoll.c 可能包含了 Linux 系统使用的事件驱动库 epoll。
#else: 如果没有定义宏 HAVE_EPOLL,则继续处理后续代码。
#ifdef HAVE_KQUEUE: 如果定义了宏 HAVE_KQUEUE,则包含文件 ae_kqueue.c。ae_kqueue.c 可能包含了 FreeBSD 或 macOS 系统使用的事件驱动库 kqueue。
#else: 如果没有定义宏 HAVE_KQUEUE,则包含文件 ae_select.c。ae_select.c 可能包含了所有系统都支持的 select 事件驱动库,但效率较低。
#endif: 结束条件编译语句块。
我们常用的CentOS使用的是epoll的实现,在Linux系统中,epoll机制是一种高效的事件触发机制,可以监听大量的文件描述符,并在文件描述符上发生事件时,立即通知应用程序。使用epoll机制时,需要使用epoll_create函数创建一个epoll对象,然后使用epoll_ctl函数向epoll对象添加或删除文件描述符,最后使用epoll_wait函数等待事件的发生。
epoll的实现在ae_epoll.c文件中:
static int aeApiCreate(aeEventLoop *eventLoop) {aeApiState *state = zmalloc(sizeof(aeApiState));if (!state) return -1;state->events = zmalloc(sizeof(struct epoll_event)*eventLoop->setsize);if (!state->events) {zfree(state);return -1;}state->epfd = epoll_create(1024); /* 1024 is just a hint for the kernel */if (state->epfd == -1) {zfree(state->events);zfree(state);return -1;}eventLoop->apidata = state;return 0;
}static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {aeApiState *state = eventLoop->apidata;struct epoll_event ee;/* If the fd was already monitored for some event, we need a MOD* operation. Otherwise we need an ADD operation. */int op = eventLoop->events[fd].mask == AE_NONE ?EPOLL_CTL_ADD : EPOLL_CTL_MOD;ee.events = 0;mask |= eventLoop->events[fd].mask; /* Merge old events */if (mask & AE_READABLE) ee.events |= EPOLLIN;if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;ee.data.u64 = 0; /* avoid valgrind warning */ee.data.fd = fd;if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1;return 0;
}
epoll_create是Linux系统中的一个系统调用,用于创建一个epoll对象,以便对文件描述符进行事件监听。
在Linux系统中,如果需要对多个文件描述符进行事件监听,常用的方式是使用select或poll函数。但是随着文件描述符数量的增加,select和poll函数的效率会逐渐降低,因为它们需要遍历所有的文件描述符,而无法实现快速的事件通知。为了解决这个问题,Linux引入了epoll机制,通过epoll_create系统调用创建一个epoll对象,然后使用epoll_ctl函数向epoll对象添加或删除文件描述符,最后使用epoll_wait函数等待事件的发生。
epoll_create函数的原型如下:
#include <sys/epoll.h>
int epoll_create(int size);
其中,size参数表示epoll对象中能够监听的最大文件描述符数量,这个参数在Linux 2.6.8之后已经无效,可以忽略。epoll_create函数返回一个整数类型的文件描述符,表示创建的epoll对象的标识符。如果创建失败,返回-1。
需要注意的是,使用epoll_create函数创建的epoll对象是在内核中创建的,而不是在用户空间中创建的。因此,在使用epoll机制时,需要将文件描述符设置为非阻塞模式,并且需要使用epoll_ctl函数向内核注册文件描述符,从而实现文件描述符的事件监听。
epoll_ctl是Linux系统中的一个系统调用,用于向epoll对象中添加或删除文件描述符,并设置对应的事件类型。
在Linux系统中,epoll机制是一种高效的事件触发机制,可以监听大量的文件描述符,并在文件描述符上发生事件时,立即通知应用程序。使用epoll机制时,需要使用epoll_create函数创建一个epoll对象,然后使用epoll_ctl函数向epoll对象添加或删除文件描述符,最后使用epoll_wait函数等待事件的发生。
epoll_ctl函数的原型如下:
#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
其中,epfd参数表示epoll对象的文件描述符,op参数表示操作类型,可以是EPOLL_CTL_ADD、EPOLL_CTL_MOD或EPOLL_CTL_DEL,分别表示添加、修改或删除文件描述符。fd参数表示要添加、修改或删除的文件描述符,event参数表示要监听的事件类型,包括读事件、写事件等。
需要注意的是,使用epoll_ctl函数添加、修改或删除文件描述符时,需要将文件描述符设置为非阻塞模式。在调用epoll_wait函数等待事件时,如果有事件发生,epoll_wait函数会返回一组事件列表,然后可以处理这些事件。处理完毕后,可以使用epoll_ctl函数修改或删除已经处理过的文件描述符,然后再次调用epoll_wait函数等待事件的发生。
epoll_wait是一个Linux内核提供的系统调用,用于等待文件描述符上的事件。epoll是Linux内核提供的一种多路I/O复用机制,可以监视多个文件描述符,一旦某个文件描述符就绪,epoll就会通知用户进程进行后续处理。
epoll_wait的函数原型如下:
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
参数说明如下:
- epfd:epoll实例的文件描述符。
- events:用于存放就绪文件描述符的数组。
- maxevents:events数组的大小。
- timeout:等待事件的超时时间,单位为毫秒。
epoll_wait的返回值如下:
- 成功时,返回就绪文件描述符的数目。
- 出错时,返回-1。
epoll_wait的使用步骤如下:
- 创建一个epoll实例,并获取其文件描述符。
- 将需要监视的文件描述符注册到epoll实例中。
- 调用epoll_wait函数,等待事件。
- 处理就绪文件描述符上的事件。
以下是epoll_wait的使用示例:
#include <sys/epoll.h>
int main() {// 创建一个epoll实例int epfd = epoll_create(1024);if (epfd == -1) {perror("epoll_create");return -1;}// 将需要监视的文件描述符注册到epoll实例中struct epoll_event event;event.events = EPOLLIN;event.data.fd = 0; // 标准输入if (epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &event) == -1) {perror("epoll_ctl");return -1;}// 等待事件struct epoll_event events[10];int nfds = epoll_wait(epfd, events, 10, -1);if (nfds == -1) {perror("epoll_wait");return -1;}// 处理就绪文件描述符上的事件for (int i = 0; i < nfds; ++i) {if (events[i].events & EPOLLIN) {// 读取标准输入char buf[1024];int n = read(events[i].data.fd, buf, sizeof(buf));if (n == -1) {perror("read");return -1;}// ...}}return 0;
}
在上述示例中,我们创建了一个epoll实例,并将标准输入注册到epoll实例中。然后,我们调用epoll_wait函数,等待标准输入上的数据到达。如果标准输入上有数据到达,epoll_wait函数就会返回,并将就绪文件描述符的相关信息保存在events数组中。最后,我们遍历events数组,处理每个就绪文件描述符上的事件。
epoll_wait是Linux内核提供的一种高效的多路I/O复用机制。它可以提高程序的并发处理能力,减少CPU占用率。
ae_epoll.c文件中封装了一系列的epoll操作,包括epoll的创建、新增、删除、扩容、等待。
那这个非阻塞IO是怎么工作的?
核心关注aeApiPoll():
static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {aeApiState *state = eventLoop->apidata;int retval, numevents = 0;retval = epoll_wait(state->epfd,state->events,eventLoop->setsize,tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1);if (retval > 0) {int j;numevents = retval;for (j = 0; j < numevents; j++) {int mask = 0;struct epoll_event *e = state->events+j;if (e->events & EPOLLIN) mask |= AE_READABLE;if (e->events & EPOLLOUT) mask |= AE_WRITABLE;if (e->events & EPOLLERR) mask |= AE_WRITABLE;if (e->events & EPOLLHUP) mask |= AE_WRITABLE;eventLoop->fired[j].fd = e->data.fd;eventLoop->fired[j].mask = mask;}}return numevents;
}
epoll_wait等待FD的就绪通知,如果FD准备完毕,则进行数据流处理,否则就阻塞等待,在Redis启动时,会在main函数中创建一个死循环,轮询监听epoll事件,当有事件就绪时,执行事件的回调函数,即我们上一篇中所讲到的,具体的命令执行函数。
ae.c aeMain()
void aeMain(aeEventLoop *eventLoop) {eventLoop->stop = 0;while (!eventLoop->stop) {if (eventLoop->beforesleep != NULL)eventLoop->beforesleep(eventLoop);aeProcessEvents(eventLoop, AE_ALL_EVENTS);}
}
int aeProcessEvents(aeEventLoop *eventLoop, int flags)
{int processed = 0, numevents;//省略部分非核心代码.....// 等待epoll就绪事件numevents = aeApiPoll(eventLoop, tvp);for (j = 0; j < numevents; j++) {aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];int mask = eventLoop->fired[j].mask;int fd = eventLoop->fired[j].fd;int rfired = 0;/* note the fe->mask & mask & ... code: maybe an already processed* event removed an element that fired and we still didn't* processed, so we check if the event is still valid. */// 核心:执行命令对应的回调函数if (fe->mask & mask & AE_READABLE) {rfired = 1;fe->rfileProc(eventLoop,fd,fe->clientData,mask);}// 核心:执行命令执行结果数据,写回客户端,回调函数if (fe->mask & mask & AE_WRITABLE) {if (!rfired || fe->wfileProc != fe->rfileProc)fe->wfileProc(eventLoop,fd,fe->clientData,mask);}processed++;}}/* Check time events */if (flags & AE_TIME_EVENTS)processed += processTimeEvents(eventLoop);return processed; /* return the number of processed file/time events */
}
就此,命令执行流程的epoll部分,就此完成,我们还是用一张图描述整个执行过程:

结语
本篇,我们对Redis源码中非阻塞的核心epoll是如何实现进行了浅析,简单了解了Redis中epoll的工作流程,至此,我们已经大体了解了Redis如何处理执行来自客户端的命令请求,但是还有一个问题我们没有清楚,Redis是如何将命令读取到的数据返回客户端的,下一篇中,我们将围绕这个问题,进行拆解,敬请期待。
相关文章:
浅析Redis②:命令处理之epoll实现(中)
写在前面 Redis作为我们日常工作中最常使用的缓存数据库,其重要性不言而喻,作为普通开发者,我们在日常开发中使用Redis,主要聚焦于Redis的基层数据结构的命令使用,很少会有人对Redis的内部实现机制进行了解,…...
react如果创建了类似于 Icketang元素,那么该如何实现 Icketang类
要实现一个类似于 "Icketang" 的类,首先需要考虑该类的属性和方法。根据上下文,可以假设 "Icketang" 是一个卡片或票据类,可以包含以下属性和方法: 属性: card_number:卡片编号amoun…...
「数字化转型」企业架构:成功业务转型的关键
在麦肯锡最近的一篇文章中,他们雄辩地论证了企业架构对数字转型的重要性。但他们也对实践状况提出了一些重要的批评。为了真正有效地支持数字转型,许多企业架构实践需要改变他们的行为。 一些EA实践首先关注的是详细记录企业的当前状态。这通常是我们在许…...
AI开启手机摄影新时代:三星Galaxy S24 Ultra影像解读
在全球科技领域,生成式AI无疑是当前最为炙手可热的亮点,不少行业专家和业界领袖都纷纷预言,生成式AI技术必将重塑千行百业。 那么是否有人想过,如果生成式AI技术被应用在智能手机上,又会带来怎样翻天覆地的变革&#x…...
Linux ---- Shell编程之函数与数组
目录 一、函数 1、函数的基本格式 2、查看函数列表 3、删除函数 4、函数的传参数 5、函数返回值 实验: 1.判断输入的ip地址正确与否 2. 判断是否为管理员用户登录 6、函数变量的作用范围 7、函数递归(重要、难点) 实验࿱…...
Python系列(9)—— 比较运算符
在Python中,比较运算符用于比较两个值的大小关系,如等于、不等于、大于、小于等。这些运算符可以帮助我们进行各种比较操作,并返回布尔值(True或False)。下面我们将详细介绍Python中的比较运算符。 等于运算符&#x…...
uni-app h5对接 thinkphp5接口跨域
uni-app h5对接 thinkphp5接口跨域 问题描述 请求接口 提示 Access to XMLHttpRequest at http://******* from origin http://localhost:8091 has been blocked by CORS policy: Response to preflight request doesnt pass access control check: It does not have HTTP o…...
react-jss书写样式
目录 react-jss的使用 react-jss的使用 实现组件化样式、动态样式、避免样式冲突 npm install react-jss yarn add react-jss// 使用 import React from react; import { createUseStyles } from react-jss;const useStyles createUseStyles({myButton: {color: green,margi…...
Oracle PL/SQL Programming 第3章:Language Fundamentals 读书笔记
总的目录和进度,请参见开始读 Oracle PL/SQL Programming 第6版 每种语言(无论是人类语言还是计算机语言)都有语法、词汇和字符集。 为了使用该语言进行交流,您必须学习管理其使用的规则。 我们许多人对学习新的计算机语言持谨慎…...
【Spring Boot 3】【@Scheduled】动态修改定时任务时间
【Spring Boot 3】【@Scheduled】动态修改定时任务时间 背景介绍开发环境开发步骤及源码工程目录结构总结背景 软件开发是一门实践性科学,对大多数人来说,学习一种新技术不是一开始就去深究其原理,而是先从做出一个可工作的DEMO入手。但在我个人学习和工作经历中,每次学习…...
WordPress如何自定义日期和时间格式?附PHP日期和时间格式字符串
WordPress网站在很多地方都需要用到日期和时间,那么我们应该在哪里设置日期和时间呢?又如何自定义日期和时间格式呢?下面boke112百科就跟大家一起来学习一下PHP标准化的日期和时间格式字符串。 特别说明:格式字符是标准化的&#…...
log4j2 配置入门介绍
配置 将日志请求插入到应用程序代码中需要进行大量的计划和工作。 观察表明,大约4%的代码专门用于日志记录。因此,即使是中等规模的应用程序也会在其代码中嵌入数千条日志记录语句。 考虑到它们的数量,必须管理这些日志语句,而…...
深入Pyecharts:桑基图绘制与炫酷效果实战【第38篇—python:桑基图】
文章目录 深入Pyecharts:桑基图绘制与炫酷效果实战桑基图简介安装 Pyecharts简单桑基图的绘制自定义桑基图的炫酷效果高级样式定制 多组数据桑基图的展示动态桑基图的绘制结合真实数据的桑基图案例导出和分享进阶应用:桑基图与其他图表的组合总结 深入Py…...
RBD —— 不同材质破碎
目录 Working with concrete Chipping Details Proxy geometry Constraints Working with glass Chipping Proxy geometry Constraints Resolving issues with glass fracturing Working with wood Clustering Using custom cutters Working with concrete Concr…...
MySql8的简单使用(1.模糊查询 2.group by 分组 having过滤 3.JSON字段的实践)
MySql8的简单使用(1.模糊查询 2.group by 分组 having过滤 3.JSON字段的实践) 一.like模糊查询、group by 分组 having 过滤 建表语句 create table student(id int PRIMARY KEY,name char(10),age int,sex char(5)); alter table student add height…...
数据监控-Prometheus/Grafana
一、数据监控Prometheus 1、什么是Prometheus Prometheus是由SoundCloud开源监控告警解决方案,从2012年开始编写代码,到2015年github上开源以来,吸引不少用户以及公司的使用。Prometheus作为新一代的开源解决方案,很多理念与Google SRE的运维之道不谋而合。 2、Promet…...
Compose | UI组件(三) | TextField() 输入框组件
文章目录 TextField() 简介TextField() 输入框例子TextField() 输入框添加装饰OutlinedTextField 边框样式输入框BasicTextField 输入框组件 总结 TextField() 简介 在 Compose 中,TextField() 组件表示文本输入框 ExperimentalMaterial3Api Composable fun TextF…...
组件冲突、data函数、组件通信
文章目录 1.组件的三大组成部分 - 注意点说明2.组件的样式冲突(用 scoped 解决)3.data是一个函数4.组件通信1.什么是组件通信?2.不同的组件关系 和 组件通信方案分类 5.prop详解prop 校验①类型校验②完整写法(类型,非…...
【C++杂货铺】详解类和对象 [上]
博主:代码菌-CSDN博客 专栏:C杂货铺_代码菌的博客-CSDN博客 目录 🌈前言🌈 📁 面向对象语言的特性 📁 类 📂 概念 📂 定义 📁 访问限定符 📂分类 &#x…...
Linux 驱动开发基础知识—— 驱动设计的思想(六)
个人名片: 🦁作者简介:一名喜欢分享和记录学习的在校大学生 🐯个人主页:妄北y 🐧个人QQ:2061314755 🐻个人邮箱:2061314755qq.com 🦉个人WeChat:V…...
多自由度冗余空间机械臂位姿一体化规划与控制【附代码】
✨ 长期致力于空间机械臂、对偶四元数、位姿一体化、路径规划、跟踪控制研究工作,擅长数据搜集与处理、建模仿真、程序编写、仿真设计。 ✅ 专业定制毕设、代码 ✅ 如需沟通交流,点击《获取方式》 (1)基于对偶四元数的冗余机械臂运…...
隧道裂缝剥落病害AI识别系统
我国现有公路隧道超2.5万座,总里程超2.8万公里,其中运营超过15年的老旧隧道占比达35%。据交通运输部2025年统计,年均因隧道结构病害导致的交通中断超1200次,直接经济损失超45亿元。传统检测模式暴露四大核心痛点:检测周…...
古戏台构件声学特性的时域有限差分方法【附模型】
✨ 长期致力于时域有限差分法、窑洞、戏台、八字墙、共形技术研究工作,擅长数据搜集与处理、建模仿真、程序编写、仿真设计。 ✅ 专业定制毕设、代码 ✅ 如需沟通交流,点击《获取方式》 (1)曲面共形网格快速生成算法: …...
解决Claude Code访问不稳定与Token不足的痛点
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 解决Claude Code访问不稳定与Token不足的痛点 许多开发者将Claude Code作为日常编程的得力助手,用于代码生成、问题调试…...
从入门到实践:EEG公开数据集分类与应用场景全解析
1. EEG公开数据集入门指南刚接触脑电信号分析的研究者,常常会被一个问题困扰:"我应该从哪里获取可靠的EEG数据?"作为一个在这个领域摸爬滚打多年的研究者,我完全理解这种困惑。记得我第一次接触EEG研究时,光…...
【DeepSeek事件驱动架构实战指南】:20年架构师亲授5大核心陷阱与避坑清单
更多请点击: https://kaifayun.com 第一章:DeepSeek事件驱动架构全景认知 DeepSeek事件驱动架构(Event-Driven Architecture, EDA)并非单一技术组件的堆叠,而是一种以事件为第一公民、强调松耦合与异步协作的系统设计…...
学术写作创新突破!2026全流程AI论文工具精选指南
2026 年 AI 论文写作工具已进入全流程闭环 学术合规时代,千笔 AI(综合评分 99 分)中文学术场景标杆;Grammarly Academic与Elicit为英文论文写作首选;按需求匹配度 - 数据可信度 - 成本承受力三维模型选型,…...
Sangfor文件夹可以删除吗?【图文讲解】深信服文件夹残留清理?如何彻底删除深信服?Sangfor文件夹是什么?
(1)问题背景打开C盘,突然冒出个Sangfor 文件夹,占用好几个 GB 空间,想删又不敢删,怕删坏系统、断网崩溃;上网一查,说法五花八门,有人说是病毒,有人说是办公软…...
Burp Suite深度解析:从流量抓包到业务逻辑漏洞挖掘
1. 这不是“学个插件”——Burp Suite 是渗透测试的呼吸系统 很多人第一次听说 Burp Suite,是在某篇“三步拿下登录框”的速成教程里:装好Java、拖进浏览器代理、点几下Repeater就弹出密码明文。结果真去测一个中型SaaS后台,不到十分钟就卡在…...
孤舟笔记 互联网常用框架篇二 Dubbo服务请求失败怎么处理?集群容错策略你用过几种
文章目录先说结论Failover:换家店试试Failfast:不行就算了Failsafe:忘了这事Failback:回头再说Forking:同时点几家Broadcast:通知所有人怎么选择回答技巧与点评加分回答面试官点评个人网站分布式系统中&…...
