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的数目时,上下文的切换就不是问题;共享资源的加锁保护,高效的半同步/半异步模式、减小锁的粒度,使用读写锁,读一块共享内存不增加开销,写内存才会锁。**

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

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

相关文章

Linux启动之uboot分析

Linux启动之uboot分析 uboot是什么&#xff1f;一、补充存储器概念1.存储器种类1.norflash - 是非易失性存储器&#xff08;也就是掉电保存&#xff09;2.nandflash - 是非易失性存储器&#xff08;也就是掉电保存&#xff09;3.SRAM - 静态随机访问存储器 - Static Random Acc…

如何在宝塔面板安装配置MySQL数据库并实现公网访问

宝塔安装MySQL数据库&#xff0c;并内网穿透实现公网远程访问 文章目录 宝塔安装MySQL数据库&#xff0c;并内网穿透实现公网远程访问前言1.Mysql服务安装2.创建数据库3.安装cpolar3.2 创建HTTP隧道 4.远程连接5.固定TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网…

【鸿蒙软件开发】ArkTS基础组件之Select(下拉菜单)、Slider(滑动条)

文章目录 前言一、Select下拉菜单1.1 子组件1.2 接口参数 1.3 属性1.4 事件1.5 示例代码 二、Slider2.1 子组件2.2 接口参数&#xff1a;SliderStyle枚举说明 2.3 属性2.4 事件SliderChangeMode枚举说明 2.5 示例代码 总结 前言 Select组件&#xff1a;提供下拉选择菜单&#…

什么是α测试β测试和灰度测试?

吃软件测试这碗饭的&#xff0c;如果基础理论都不懂&#xff0c;谈何长久&#xff1f; 欢迎来学习本系列&#xff0c;基础理论比较枯燥&#xff0c;这也是为什么现在很少人掌握的主要原因。热饭尽量用浅显易懂 生动的例子 来帮助大家学习基础理论&#xff0c;所以请耐心看完此系…

【Linux】:Linux开发工具之Linux编辑器vim的使用

&#x1f52b;1.Linux编辑器-vim使用 &#x1f4e4; vi/vim的区别简单点来说&#xff0c;它们都是多模式编辑器&#xff0c;不同的是vim是vi的升级版本&#xff0c;它不仅兼容vi的所有指令&#xff0c;而且还有一些新的特性在里面。例如语法加亮&#xff0c;可视化操作不仅可以…

关于Anaconda及其镜像源的相关问题

1. 创建的虚拟环境中没有bin文件 conda create -n test_env请在上诉代码后添加对应的python对应版本&#xff0c;即可创建成功 conda create -n test_env python3.82. 关于anaconda中镜像源的相关操作 设置pip的全局索引源为阿里云镜像&#xff08;注意是全局索引&#xff0…

“软件开发报价混乱?看这一篇就够了!“

大家好&#xff0c;今天我们要聊一聊软件开发报价的那些事儿。相信很多企业和个人都曾为此犯过愁&#xff0c;看着报价单上一串串数字&#xff0c;心里直犯嘀咕&#xff1a;这价格靠谱吗&#xff1f;是不是被忽悠了&#xff1f;别急&#xff0c;今天我们就来揭开软件开发报价的…

linux 模块安装与卸载

文章目录 模块实现编译模块的 makefile编译报错解决模块编译日志自动化模块安装模块卸载配置头文件路径C/C 插件clangd 插件 模块实现 新建 my_module.c 文件 #include <linux/types.h> #include <linux/kernel.h> #include <linux/init.h> #include <l…

Flutter报错RenderBox was not laid out: RenderRepaintBoundary的解决方法

文章目录 报错问题分析问题原因 解决办法RenderBox was not laid out错误的常见原因常见原因解决方法 RenderRepaintBoundaryRenderRepaintBoundary用途 报错 RenderBox was not laid out: RenderRepaintBoundary#d4abf relayoutBoundaryup1 NEEDS-PAINT NEEDS-COMPOSITING-BI…

海南海口大型钢结构件3D扫描全尺寸三维测量平面度平行度检测-CASAIM中科广电

高精度三维扫描技术已经在大型工件制造领域发挥着重要作用&#xff0c;特别是在质量检测环节&#xff0c;高效、高精度&#xff0c;可以轻松实现全尺寸三维测量。本期&#xff0c;CASAIM要分享的应用是在大型钢结构件的关键部位尺寸及形位公差检测。 钢结构件&#xff0c;是将…

功能型前端项目技术栈选型

PC功能型官网技术栈选择 vue2vuexvue-routerxaxiosanimate主推&#xff1a;vue3piniavue-routertypeScriptaxiosanimate&#xff08;新技术后期踩坑多&#xff0c;成本较高&#xff09; 1.2 vue3对比vue2 团队已经熟悉 Vue 2&#xff0c;并且官网的规模不是很大&#xff0c;Vue…

使用 Visual Studio Code 编写 TypeScript程序

安装 TypeScript 首先&#xff0c;确保你已经安装了 TypeScript&#xff0c;如果没有安装&#xff0c;请参考https://blog.csdn.net/David_house/article/details/134077973?spm1001.2014.3001.5502进行安装 创建 新建一个文件夹&#xff0c;用vs code打开&#xff0c;在文…

【MySQL--->内外连接】

文章目录 [TOC](文章目录) 一、内连接二、左外连接三、右外连接 一、内连接 内连接就是将两个表连接进行笛卡尔积查询 显示SMITH的名字和部门名称 二、左外连接 左外连接就是以左面的表为主&#xff0c;即便是右边的表没有而左边表项中有的&#xff0c;依然显示 查询所有学…

C# “依赖注入” 中的 “三种生命周期”

&#x1f680;简介 依赖注入&#xff08;Dependency Injection&#xff0c;简称DI&#xff09;是一种实现控制反转&#xff08;IoC&#xff09;的技术&#xff0c;用于减少代码之间的耦合度。通过依赖注入&#xff0c;一个类可以从外部获取其依赖的对象&#xff0c;而不是自己…

评估在线不平衡学习的PAUC

评估在线不平衡学习的PAUC 原始论文《Prequential AUC: properties of the area under the ROC curve for data streams with concept drift》 由于正常的AUC需要计算整体数据集上&#xff0c;每个数据的预测置信度的排名。那么我们首先要求我们的在线学习算法在进行预测时也返…

实体店做商城小程序如何

互联网电商深入各个行业&#xff0c;传统线下店商家无论产品销售还是服务业&#xff0c;仅靠以往的经营模式&#xff0c;很难拓展到客户&#xff0c;老客流失严重&#xff0c;同时渠道单一&#xff0c;无法实现外地客户购物及线上客户赋能等。 入驻第三方平台有优势但也有不足…

审核 Microsoft SQL Server 日志

手动审核数据库活动是一项艰巨的任务&#xff0c;有效完成审计的最佳方法是使用简化和自动化数据库监控的综合解决方案&#xff0c;该解决方案还应使数据库管理员能够监控、跟踪和即时识别任何操作问题的根本原因&#xff0c;并实时检测对机密数据的未经授权的访问。 什么是 S…

Java on Azure Tooling 9月更新|Azure OpenAI 服务和 Playground 支持及更多

作者&#xff1a;Jialuo Gan - Program Manager, Developer Division at Microsoft 排版&#xff1a;Alan Wang 大家好&#xff0c;欢迎阅读 Java on Azure 开发者工具九月份更新。在本次更新中&#xff0c;我们将介绍对 Azure OpenAI 服务和 Playground 的支持&#xff0c;开发…

<C++> vector模拟实现

目录 前言 一、定义命名空间 二、构造函数 三、拷贝构造 四、赋值运算符重载 五、push_back && reserve 六、深拷贝问题 七、iterator 迭代器 1. 可读可写 2. 只读 八、operator[ ] 1. 可读可写 2. 只读 九、insert 问题&#xff1a;内部迭代器失效 十、erase 十一、re…

《RT-DETR改进实战》专栏介绍 专栏目录

《RT-DETR改进实战专栏》介绍及目录 介绍&#xff1a;欢迎来到最新专栏《RT-DETR改进实战》&#xff01;这个专栏专注于基于 YOLOv8 项目的魔改版本&#xff0c;而不是百度飞桨框架中的 RT-DETR。 本专栏为想通过改进 RT-DETR 算法发表论文的同学设计。每篇文章均包含完整的改…