当前位置: 首页 > news >正文

Linux高性能服务器编程——ch8笔记

第8章 高性能服务器程序框架

8.1 服务器模型


服务器启动后,首先创建一个(或多个)监听socket,并调用bind函数将其绑定到服务器感兴趣的端口,然后调用listen函数等待客户连接。服务器稳定运行之后,客户端就可以调用connect函数向服务器发起连接。由于客户连接请求是随机到达的异步事件,版务器需要使用某种I/O模型来监听这一事件。
下图服务器使用的是I/O复用技术之一的select系统调用。当监听到连接请求后,服务器就调用accept函数接受它,并分配一个逻辑单元为新的连接服务。逻辑单元可以是新创建的子进程、子线程或者其他,下图服务器给客户端分配的逻辑单元是fork系统调用创建的子进程。逻辑单元读取客户请求,处理该请求,然后将处理结果返回给客户端,客户端接收到服务器反馈的结果之后,可以继续向服务器发送请求,也可以主动关闭连接。如果客户端主动关团连接,则服务器执行被动关闭连接。至此,双方的通信结束。服务器同时监听多个客户请求是通过select系统调用实现的。

访问量过大时,响应较慢。
P2P(Peer to Peer,点对点)模型使得每台机器在消耗服务的同时也给别人提供服务,每台主机既是客户端,也是服务器,专门的发现服务器用来提供查找服务。

8.2 服务器编程框架


image.pngimage.png

8.3 I/O模型

socket创建时默认是阻塞的。阻塞的概念也能应用于文件描述符,(非)阻塞的文件描述符称为(非)阻塞I/O。
针对阻塞I/O执行的系统调用可能因为无法立即完成而被OS挂起;针对非阻塞I/O执行的系统调用则总是立即返回,而不管事件是否已经发生。
只有在事件已经发生的情况下操作非阻塞I/O,才能提高效率,非阻塞I/O要和其他I/O通知机制(I/O复用和SIGIO信号)一起使用。
同步I/O(阻塞I/O、I/O复用和信号驱动I/O)要求用户代码自行执行I/O 操作(将数据从内核缓冲区读入用户缓冲区,或将数据从用户缓冲区写入内核缓冲区),而异步 I/O机制则由内核来执行I/O操作(数据在内核缓冲区和用户缓冲区之间的移动是用内核在“后台”完成的。
同步I/O向应用程序通知的是I/O就绪事件,由应用程序完成I/O读写;而异步I/O向应用程序通知的是I/O完成事件,由内核完成I/O读写。
image.png

8.4 两种高效的事件处理模式

三类事件:I/O事件、信号事件、定时事件。
Reactor事件处理模式:要求主线程(I/O处理单元)只负责监听文件描述上是否有事件发生。有则立即将事件通知工作线程(逻辑单元),然后读写数据,接受新连接,以及处理客户请求。
同步I/O模型(epoll_wait为例)实现:
1)主线程往epoll内核事件表中注册socket上的读就绪事件;
2)主线程调用epoll_wait等待socket上有数据可读;
3)socket上有数据时,epoll_wait通知主线程将socket可读事件放入请求队列;
4)睡眠在请求队列上的某个工作线程被唤醒,从socket上读取数据,处理客户请求,然后往epoll内核事件表中注册该socket上的写就绪事件。
5)主线程调用epoll_wait等待socket可写。
6)socket可写时,epoll_wait通知主线程,将socket可写事件放入请求队列;
7)睡眠在请求队列上的某个工作线程被唤醒,它往socket上写入服务器处理客户请求的结果。
image.png
Proactor事件处理模式:将所有I/O操作交给主线程和内核处理,工作线程仅负责业务逻辑。
异步I/O模型(aio_read和aio_write为例)实现:
1)主线程调用aio_read函数向内核注册socket上的读完成事件,并告诉内核用户读缓冲区的位置,以及读操作完成时如何通知应用程序(信号为例);
2)主线程继续处理其他逻辑;
3)当socket上的数据被读入用户缓冲区后,内核向应用程序发送信号,以通知应用程序数据已经可用;
4)应用程序预先定义好的信号处理函数选择一个工作线程来处理客户请求。工作线程处理完客户请求之后,调用aio_write函数向内核注册socket上的写完成事件,并告诉内核用户写缓冲区的位置,以及写操作完成时如何通知应用程序(信号为例);
5)主线程继续处理其他逻辑;
6)用户缓冲区的数据被写入socket后,内核将向应用程序发送信号,通知应用程序数据发送完毕;
7)应用程序预先定义好的信号处理函数选择一个工作线程来做善后处理,比如决定是否关闭socket。
image.png
使用同步I/O模型模拟Proactor模式:
1)主线程往epoll内核事件表中注册socket上的读就绪事件;
2)主线程调用epoll_wait等待socket上有数据可读;
3)当socket上有数据可读时,epoll_wait通知主线程。主线程从socket循环读取数据,直到没有更多数据可读,然后将读取到的数据封装成一个请求对象,插入请求队列;
4)睡眠在请求队列上的某个工作线程被唤醒,它获得请求对象并处理客户请求,然后往epoll内核事件表中注册socket上的写就绪事件。
5)主线程调用epoll_wait等待socket可写;
6)当socket可写时,epoll_wait通知主线程。主线程往socket上写入服务器处理客户请求的结果。
image.png

8.5 两种高效的并发模式

这里讨论模式。并发模式指I/O处理单元和多个逻辑单元之间协调完成任务。
半同步/异步模式:这里的同步异步指程序的执行是按顺序还是由系统事件驱动。
image.png
半同步/半异步模式中,同步线程用于处理客户逻辑,相当于逻辑单元;异步线程用于处理I/O事件,相当于I/O处理单元。异步线程监听到客户请求后,就将其封装成请求对象并插入请求队列中,请求队列将通知某个工作在同步模式的工作线程(选取方法:Round Robin、条件变量、信号量)读取并处理该请求对象。
image.png
其变体:半同步/半反应堆模式。
image.png
半同步/半反应堆模式采用的事件处理模式是Reactor模式:要求工作线程从socket上读取客户请求和往socket写入服务器应答。也可以使用模拟的Proactor事件处理模式。
缺点:
1)主线程和工作线程共享请求队列,添加和取出任务都要对请求队列加锁保护,耗费CPU时间;
2)每个工作线程在同一时间只能处理一个客户请求,客户多,则队列堆积,客户端响应慢,若增加工作线程,则工作线程的切换也会耗费CPU时间。
image.png
主线程只管理监听socket,工作线程管理连接socket。派发方式:往主线程和工作线程之间的管道里写数据,工作线程检测到管道上有数据可读时,就分析是否是一个新的客户连接请求到来。如果是,则把该新soeket上的读写事件注册到自己的epoll内核事件表中。
领导者/追随者模式:多个工作线程轮流获得事件源集合,轮流监听、分发并处理事件。在任意时间点,程序都仅有一个领导者线程,负责监听I/O事件,而其他线程则都是追随者,它们休眠在线程池中等待成为新的领导者,当前的领导者如果检测到 I/O事件,首先要从线程池中推选出新的领导者线程,然后处理I/O事件。此时,新的领导者等得新的I/O事件,而原来的领导者则处理I/O事件,二者实现了并发。
组件:句柄集(HandleSet)、线程集(ThreadSet)、事件处理器(EventHandler)、具体的事件处理器(ConcreteEventHandler)。
image.png
句柄集:句柄表示I/O资源(文件描述符),句柄集用wait_for_event方法监听句柄上的I/O事件,将就绪事件通知领导者线程。领导者则调用绑定到Handle上的事件处理器来处理事件。领导者将Handle和事件处理器绑定是通过调用句柄集中的register_handle方法实现。
线程集:所有工作线程管理者。负责个线程之间的同步,及新领导者推选。线程集中线程的三种状态:Leader(领导者)、Processing(处理事件中)、Follower(追随者)。
image.png
事件处理器和具体的事件处理器:事件处理器通常包含回调函数,用于处理事件对应的业务逻辑,使用前被绑定到句柄上。具体的事件处理器是其派生类,必须重新实现基类的handle_event方法来处理特定任务。
image.png

8.6 有限状态机

用于逻辑单元内部。有的应用层协议头部包含数据包类型字段,每种类型可以映射为逻辑单元的一种执行状态,服务器可以根据它来编写相应的处理逻辑,通过内部驱动实现状态转移。
很多网络协议,包括TCP协议和IP协议,都在其头部中提供头部长度字段。程序根据该字段值就可以知道是否接收到一个完整的协议头部。但HTTP协议并未提供这样的头部长度字段,并且其头部长度变化也很大。
判断HTTP头部结束的依据:遇到一个空行仅包含一对回车换行符(<CR><LF>)。如果一次读操作没有读入HTTP请求的整个头部,即没有遇到空行,那么必须等待客户继续写数据并再次读入。不过在寻找空行的过程中,可以同时完成对整个HTTP请求头部的分析(空行前面还有请求行和头部域),以提高解析HTTP请求的效率。
使用主、从两个有限状态机实现最简单的HTTP请求的读取和分析:

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>#define BUFFER_SIZE 4096    /* 读缓冲区大小 */
/* 主状态机的两种可能状态,分别表示:当前正在分析请求行, 当前正在分析头部字段 */
enum CHECK_STATE{CHECK_STATE_REQUESTLINE = 0,CHECK_STATE_HEADER
};/* 从状态机的三种可能状态,即行的读取状态,分别表示:读取到一个完整的行、行出错* 和行数据尚且不完整 */
enum LINE_STATUS{LINE_OK = 0,LINE_BAD,LINE_OPEN
};/* 服务器处理HTTP请求的结果: NO_REQUEST表示请求不完整,需要继续读取客户数据;* GET_REQUEST表示获得了一个完整的客户请求;* BAD_REQUEST表示客户请求有语法错误;* FORBIDDEN_REQUEST表示客户对资源没有足够的访问权限;* INTERNAL_ERROR表示服务内部错误;* CLOSED_CONNECTION表示客户端已经关闭连接了 */
enum HTTP_CODE{NO_REQUEST,GET_REQUEST,BAD_REQUEST,FORBIDDEN_REQUEST,INTERNAL_ERROR,CLOSED_CONNECTION
};/* 为了监护问题,我们没有给客户端发送一个完整的HTTP应答报文,* 而只是根据服务器的处理结果发送如下成功或失败信息 */
static const char *szret[] = {"I get a correct result\n",
"Somethin wrong\n"};/* 从状态机,用于解析出一行内容 */
LINE_STATUS parse_line(char *buffer, int &checked_index, int &read_index)
{char temp;/* checked_index指向buffer(应用程序的读缓冲区)中当前正在分析的字节,* read_index指向buffer中客户数据的尾部的下一字节,buffer中第0~checked_index* 字节都已经分析完毕,第checked_index~(read_index - 1)字节由下面的循环挨个分析 */for (; checked_index < read_index; ++checked_index){/* 获得当前要分析的字节 */temp = buffer[checked_index];/* 如果当前的字节是"\r", 即回车符,则说明可能读取到一个完整的行 */if (temp == '\r'){/* 如果"\r"字符碰巧是目前buffer中的最后一个已经被读入的客户数据,* 那么分析没有读取到一个完整的行,返回LINE_OPEN以表示还需要继续* 读取客户数据才能进一步分析 */if ((checked_index + 1) == read_index){return LINE_OPEN;}/* 如果下一个字符是“\n”,则说明我们成功读取到一个完整的行 */else if (buffer[checked_index + 1] == '\n'){buffer[checked_index++] = '\0';buffer[checked_index++] = '\0';return LINE_OK;}/* 否则的话,说明客户范松的HTTP请求存在语法问题 */return LINE_BAD;}/* 如果当前的字节是"\n", 即换行符,则也说明可能读取到一个完整的行 */else if (temp == '\n'){if ((checked_index > 1) && buffer[checked_index - 1] == '\r'){buffer[checked_index++] = '\0';buffer[checked_index++] = '\0';return LINE_OK;}return LINE_BAD;}}/* 如果所有内容都分析完毕也没有遇到"\r"字符,则返回LINE_OPEN,* 表示还需要继续读取客户数据才能进一步分析 */return LINE_OPEN;
}/* 分析请求行 */
HTTP_CODE parse_requestline(char *temp, CHECK_STATE &checkstate)
{char *url = strpbrk(temp, " \t");/* 如果请求航中没有空白字符或“\t”字符,则HTTP请求必有问题 */if (!url){return BAD_REQUEST;}*url++ = '\0';char *method = temp;if (strcasecmp(method, "GET") == 0) /* 仅支持GET方法 */{printf("The request method is GET\n");}else{return BAD_REQUEST;}url += strspn(url, " \t");char *version = strpbrk(url, " \t");if (!version){return BAD_REQUEST;}*version++ = '\0';version += strspn(version, " \t");/* 仅支持HTTP/1.1 */if (strcasecmp(version, "HTTP/1.1") != 0){return BAD_REQUEST;}/* 检查URL是否合法 */if (strncasecmp(url, "http://", 7) == 0){url += 7;url = strchr(url, '/');}if (!url || url[0] != '/'){return BAD_REQUEST;}printf("The request URL is %s\n", url);/* HTTP 请求行处理完毕,状态转移到头部字段的分析 */checkstate = CHECK_STATE_HEADER;return NO_REQUEST;
}/* 分析头部字段 */
HTTP_CODE parse_headers(char *temp)
{/* 遇到一个空行,说明我们得到了一个正确的HTTP请求 */if (temp[0] == '\0'){return GET_REQUEST;}else if (strncasecmp(temp, "Host:", 5) == 0) /* 处理"HOST"头部字段 */{temp += 5;temp += strspn(temp, " \t");printf("the request host is: %s\n", temp);}else /* 其他头部字段暂不处理 */{printf("I can not handle this header\n");}return NO_REQUEST;
}/* 分析HTTP请求的入口函数 */
HTTP_CODE parse_content(char *buffer, int &checked_index, CHECK_STATE &checkstate,int &read_index, int &start_line)
{LINE_STATUS linestatus = LINE_OK;   /* 记录当前行的读取状态 */HTTP_CODE retcode = NO_REQUEST;     /* 记录HTTP请求的处理结果 *//* 主状态机,用于从buffer中取出所有完整的行 */while ((linestatus == parse_line(buffer, checked_index, read_index)) == LINE_OK){char *temp = buffer + start_line;   /* start_line是行在buffer中的起始位置 */start_line = checked_index;     /* 记录下一行的起始位置 *//* checkstate 记录主状态机当前的状态 */switch(checkstate){case CHECK_STATE_REQUESTLINE:   /* 第一个状态,分析请求行 */{retcode = parse_requestline(temp, checkstate);if (retcode == BAD_REQUEST){return BAD_REQUEST;}break;}case CHECK_STATE_HEADER:    /* 第二个状态,分析头部字段 */{retcode = parse_headers(temp);if (retcode == BAD_REQUEST){return BAD_REQUEST;}else if (retcode == GET_REQUEST){return GET_REQUEST;}break;}default:{return INTERNAL_ERROR;}}}/* 若没有读取到一个完整的行,则表示还需要继续读取客户数据才能进一步分析 */if (linestatus == LINE_OPEN){return NO_REQUEST;}else{return BAD_REQUEST;}
}int main(int argc, char *argv[])
{if (argc < 2){printf("usage: %s ip_address port_number \n", basename(argv[0]));return 1;}const char *ip = argv[1];int port = atoi(argv[2]);struct sockaddr_in address;bzero(&address, sizeof(address));address.sin_family = AF_INET;inet_pton(AF_INET, ip, &address.sin_addr);address.sin_port = htons(port);int listenfd = socket(PF_INET, SOCK_STREAM, 0);assert(listenfd >= 0);int ret = bind(listenfd, (struct sockaddr *)&address, sizeof(address));assert(ret != -1);ret = listen(listenfd, 5);assert(ret != -1);struct sockaddr_in client_address;socklen_t client_addrlength = sizeof(client_address);int fd = accept(listenfd, (struct sockaddr *)&client_address, &client_addrlength);if (fd < 0){printf("errno is : %d\n", errno);}else{char buffer[BUFFER_SIZE];   /* 读缓冲区 */memset(buffer, '\0', BUFFER_SIZE);int data_read = 0;int read_index = 0;     /* 当前已经读取了多少字节的客户数据 */int checked_index = 0;  /* 当前已经分析完了多少字节的客户数据 */int start_line = 0;      /* 行在buffer中的起始位置 *//* 设置主状态机的初始状态 */CHECK_STATE checkstate = CHECK_STATE_REQUESTLINE;while (1)   /* 循环读取客户数据并分析之 */{data_read = recv(fd, buffer + read_index, BUFFER_SIZE - read_index, 0);if (data_read == -1){printf("reading failed\n");break;}else if (data_read == 0){printf("remote client has closed the connection\n");break;}read_index += data_read;/* 分析目前已经获得的所有客户数据 */HTTP_CODE result = parse_content(buffer, checked_index, checkstate,read_index, start_line);    /* 尚未得到一个完整的HTTP请求 */if (result == NO_REQUEST){continue;}else if (result == GET_REQUEST)     /* 得到一个完整的、正确的HTTP请求 */{send(fd, szret[0], strlen(szret[0]), 0);break;}else    /* 其他情况表示发生错误 */{send(fd, szret[1], strlen(szret[1]), 0);break;}}close(fd);}close(listenfd);return 0;
}

image.png
image.png

8.7 提高服务器性能的其他建议

软件上:系统软件资源(OS允许用户打开的最大文件描述符数量)、服务器程序本身。
:用服务器硬件资源换取运行效率。池是一组资源的集合,在服务器启动时就被完全创建好并初始化(静态资源分配)。直接从池中获取资源,也可在处理完客户连接后放回池中。池相当于服务器管理系统资源的应用层设施,避免对内核的频繁访问。内存池:用于socket的接收缓存和发送缓存。比如HTTP请求,预先分配足够大的接收缓存区。
进程池、线程池:需要工作进程或线程处理客户请求时,从池中取得执行实体,无须动态调用fork或pthread_create。
连接池:用于服务器或服务器机群的内部永久连接。如服务器预先和DB建立连接。
数据复制:内核直接处理从socket或者文件读入的数据,如ftp服务器使用”零拷贝“函数sendfile;使用共享内存,而不是管道或消息队列;指针。
上下文切换和锁:进程切换或线程切换导致的的系统开销,半同步/半异步模式(一个线程同时处理多个客户连接)、多线程服务器的一个优点是不同的线程可以同时运行在不同的CPU上,当线程的数量不大于CPU的数目时,上下文的切换就不是问题;共享资源的加锁保护,高效的半同步/半异步模式、减小锁的粒度,使用读写锁,读一块共享内存不增加开销,写内存才会锁。**

相关文章:

Linux高性能服务器编程——ch8笔记

第8章 高性能服务器程序框架 8.1 服务器模型 服务器启动后&#xff0c;首先创建一个&#xff08;或多个&#xff09;监听socket&#xff0c;并调用bind函数将其绑定到服务器感兴趣的端口&#xff0c;然后调用listen函数等待客户连接。服务器稳定运行之后&#xff0c;客户端就可…...

Android WMS——ViewRootImpl分析(六)

一、简介 ViewRootImpl是View中的最高层级,属于所有View的根(但ViewRootImpl不是View,只是实现了ViewParent接口),维护了整个视图结构,并作为输入事件的分发器和绘图管道的输入端点,承担着输入事件分发、窗口管理、视图绘制和系统事件响应等关键角色。对于Android应用程…...

Unsatisfied dependency expressed through bean property ‘sqlSessionTemplate‘;

代码没有问题&#xff0c;但是启动运行报错 2023-10-25 16:59:38.165 INFO 228964 --- [ main] c.h.h.HailiaowenanApplication : Starting HailiaowenanApplication on ganluhua with PID 228964 (D:\ganluhua\code\java\hailiao-java\target\classes …...

【C++】智能指针:auto_ptr、unique_ptr、share_ptr、weak_ptr(技术介绍 + 代码实现)(待更新)

文章目录 0. 概述智能指针&#xff0c;智能在哪儿&#xff1f;RAII 的介绍四个智能指针的特点&#xff1a; 1. auto_ptr&#xff08;C98&#xff09;&#x1f40e;核心功能的简单实现 2. unique_ptr&#xff08;C11&#xff09;&#x1f40e;核心功能的简单实现 3. shared_ptr&…...

nodejs+vue全国公考岗位及报考人数分析

传统的搜索引擎尽管解决了信息搜索问题&#xff0c;但无法进行有效的数据分析和优质资源的获取。并且&#xff0c;人们的需求不同&#xff0c;数据的要求也不同。为了解决这一问题&#xff0c;定向抓取数据的爬虫诞生了。它的诞生把人们从重复性的劳动中解放出来&#xff0c;节…...

【0基础学Java第二课】数据类型与变量

2. 数据类型与变量 2.1 字面常量2.2 数据类型2.3 变量2.3.1 变量概念2.3.2 语法格式 2.4 整型变量2.4.1 整型变量2.4.2 长整型变量2.4.3 短整型变量2.4.4 字节型变量 2.5 浮点型变量2.6 字符型2.7 布尔型变量2.8 类型转换2.9 类型提升2.10 字符串类型2.10.1 字符串拼接操作符 2…...

Pytorch整体工作流程代码详解(新手入门)

一、前言 本文详细介绍Pytorch的基本工作流程及代码&#xff0c;以及如何在GPU上训练模型&#xff08;如下图所示&#xff09;包括数据准备、模型搭建、模型训练、评估及模型的保存和载入。 适用读者&#xff1a;有一定的Python和机器学习基础的深度学习/Pytorch初学者。 本文…...

读图数据库实战笔记02_图数据建模

1. 概念 1.1. 实体 1.1.1. 通常用名词来表示 1.1.2. 描述一个领域中的事物或者事物类型 1.1.2.1. 汽车 1.1.2.2. 用户 1.1.2.3. 地理位置 1.1.3. 在逻辑模型和技术实现过程中&#xff0c;实体通常会变成“顶点” 1.2. 关系 1.2.1. 用动词&#xff08;或动词短语&#…...

竞赛 深度学习卫星遥感图像检测与识别 -opencv python 目标检测

文章目录 0 前言1 课题背景2 实现效果3 Yolov5算法4 数据处理和训练5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **深度学习卫星遥感图像检测与识别 ** 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐…...

对Happens-Before的理解

Happens-Before Happens-Before 是一种可见性模型&#xff0c;也就是说&#xff0c;在多线程环境下。原本因为指令重排序的存在会导致数据的可见性问题&#xff0c;也就是 A 线程修改某个共享变量对 B 线程不可见。因此&#xff0c;JMM 通过 Happens-Before 关系向开发人员提供…...

分类预测 | MATLAB实现SSA-CNN-BiGRU麻雀算法优化卷积双向门控循环单元数据分类预测

分类预测 | MATLAB实现SSA-CNN-BiGRU麻雀算法优化卷积双向门控循环单元数据分类预测 目录 分类预测 | MATLAB实现SSA-CNN-BiGRU麻雀算法优化卷积双向门控循环单元数据分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.MATLAB实现SSA-CNN-BiGRU麻雀算法优化卷积双…...

Java面试八股文之暑假合集

八股文暑假合集 基础篇二分查找 java基础篇7月12号面向对象和面向过程的区别重载和重写String 7月13号自动装箱和拆箱静态方法构造方法成员变量和局部变量对象引用和对象实例返回值 与equals(重要)hashcode()和equals()HashMap 7月16号线程&#xff0c;进程和程序final关键字的…...

竞赛选题 深度学习卫星遥感图像检测与识别 -opencv python 目标检测

文章目录 0 前言1 课题背景2 实现效果3 Yolov5算法4 数据处理和训练5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **深度学习卫星遥感图像检测与识别 ** 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐…...

机器学习第一周

一、概述 机器学习大致会被划分为两类&#xff1a;监督学习&#xff0c;无监督学习 1.1 监督学习 监督学习其实就是&#xff0c;给计算机一些输入x和正确的输出y&#xff08;训练数据集&#xff09;&#xff0c;让他总结x->y的映射关系&#xff0c;从而给他其他的输入x&a…...

大数据采集技术与预处理学习一:大数据概念、数据预处理、网络数据采集

目录 大数据概念&#xff1a; 1.数据采集过程中会采集哪些类型的数据&#xff1f; 2.非结构化数据采集的特点是什么&#xff1f; 3.请阐述传统的数据采集与大数据采集的区别&#xff1f; ​​​​​​​ ​​​​​​​4.大数据采集的数据源有哪些&#xff1f;针对不同的数…...

MySQL - 为什么索引结构默认使用B+树,而不是其他?

B-Tree的缺点&#xff1a; 范围查询效率相对较低&#xff1a;虽然B-Tree支持范围查询&#xff0c;但在实际操作中可能需要进行多次树遍历&#xff0c;性能较差。磁盘空间利用不高&#xff1a;B-Tree中的非叶子节点也存储数据&#xff0c;导致磁盘空间利用率相对较低。更复杂的平…...

信息系统项目管理师教程 第四版【第3章-信息系统治理-思维导图】

信息系统项目管理师教程 第四版【第3章-信息系统治理-思维导图】...

2023.NET技术沙龙知识学习笔记

目录 一.Bootstrap Blazor UI组件库企业级应用介绍1.Blazor是什么2.为什么要用Blazor3.Bootstrap Blazor是什么 二.使用WebAssembly运行、扩展.NET应用程序1.WebAssembly简介2.WebAssembly的起源3.为什么选择二进制格式&#xff1f;4.WebAssembly与传统JavaScript的对比5.执行速…...

Golang教程——配置环境,再探GoLand

文章目录 一、Go是什么&#xff1f;二、环境配置验证配置环境变量 三、安装开发者工具GoLand四、HelloGolang 一、Go是什么&#xff1f; Go&#xff08;也称为Golang&#xff09;是一种开源的编程语言&#xff0c;由Google开发并于2009年首次发布。Go语言旨在提供一种简单、高…...

C++之lambda匿名、using、typedef总结【全】(二百四十九)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…...

基于SpringBoot的个人博客系统

基于SpringBootVue的个人博客系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatisVue工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 主页 系统公告 博客详情 后台发文 摘要 基于Spring Boot的个人博客系统是一种…...

javascript中的继承

基本术语 本文中&#xff0c;proto [[Prototype]] 原型链 基本思想&#xff1a; 构造函数生成的对象有一个指针&#xff08;proto&#xff09;指向构造函数的原型。如果将构造函数1的原型指向另一个构造函数2的实例&#xff0c;则构造函数1的实例__proto__.proto 指向了构…...

智能问答技术在百度搜索中的应用

作者 | Xiaodong 导读 本文主要介绍了智能问答技术在百度搜索中的应用。包括机器问答的发展历程、生成式问答、百度搜索智能问答应用。欢迎大家加入百度搜索团队&#xff0c;共同探索智能问答技术的发展方向&#xff0c;文末有简历投递方式。 全文6474字&#xff0c;预计阅读时…...

STM32F4X SDIO(一) SD卡介绍

STM32F4X SDIO&#xff08;一&#xff09; SD卡介绍 SD卡分类外观分类容量分类传输速度分类 在之前的章节中&#xff0c;讲过有关嵌入式的存储设备&#xff0c;有用I2C驱动的EEPROM、SPI驱动的FLASH和MCU内部的FLASH&#xff0c;这类存储设备的优点是操作简单&#xff0c;但是缺…...

10分钟了解JWT令牌 (JSON Web)

10分钟了解JSON Web令牌&#xff08;JWT&#xff09; JSON Web Token&#xff08;JWT&#xff09;是目前最流行的跨域身份验证解决方案。今天给大家介绍JWT的原理和用法。 1.跨域身份验证 Internet服务无法与用户身份验证分开。一般过程如下。 1.用户向服务器发送用户名和密码。…...

【经验总结】ECU系统休眠后通过诊断报文唤醒ECU且唤醒网络后快发NM报文

目录 前言 正文 1.CanNM状体机分析 2.ComM状态机分析 3.解决方案 4.总结 前言...

基于Android 10系统的ROC-RK3399-PC Pro源码编译

基于Android 10系统的ROC-RK3399-PC Pro源码编译 一、开发环境搭建二、下载Android 10 SDK三、编译Android 10 SDK ROC-RK3399-PC Pro资料下载处&#xff1a;https://www.t-firefly.com/doc/download/145.html一、开发环境搭建 Android 10 SDK的编译对PC机的要求不低&#xff…...

网络滤波器/网络滤波器/脉冲变压器要怎样进行测试,一般要测试哪些参数?

Hqst华强盛导读&#xff1a;网络滤波器/网络滤波器/脉冲变压器要怎样进行测试&#xff0c;一般要测试哪些参数&#xff1f;测试网络滤波器的测试方法和步骤如何&#xff0c;需用到哪些测试工具和仪器设备呢&#xff1f; 一&#xff0c;网络流量的监控和过滤能力测试&am…...

基于vue天气数据可视化平台

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…...

Go 语言常见的 ORM 框架

ORM&#xff08;Object-Relational Mapping&#xff09;是一种编程技术&#xff0c;用于将面向对象编程语言中的对象模型和关系数据库中的数据模型相互映射。ORM框架可以把数据操作从 SQL 语句中抽离出来&#xff0c;将关系型数据库中的表映射成对象&#xff0c;通过面向对象的…...