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

I/O多路复用三种实现

 

一.select 实现

(1)select流程

基本流程是:

1. 先构造一张有关文件描述符的表;                     fd_set readfds

2. 清空表                                                              FD_ZERO()

3. 将你关心的文件描述符加入到这个表中;           FD_SET()

4. 调用select函数。                                              selset()

5. 判断是哪一个或哪些文件描述符产生了事件(IO操作);   FD_ISSET()

6. 做对应的逻辑处理;       

(2)selset函数

头文件: #include<sys/select.h>   #include<sys/time.h>   

             #include<sys/types.h>   #include<unistd.h>

声明: int select(int nfds, fd_set *readfds, fd_set *writefds,\

                                    fd_set *exceptfds, struct timeval *timeout);

功能:监测是哪些文件描述符产生事件,阻塞等待产生.

参数:nfds:    监测的最大文件描述个数(文件描述符从0开始,这里是个数,记得+1)

          readfds:  读事件集合; // 键盘鼠标的输入,客户端连接都是读事件

          writefds: 写事件集合;  //NULL表示不关心

          exceptfds:异常事件集合;  //NULL 表示不关心

          timeout:   设为NULL,等待直到某个文件描述符发生变化;

                              设为大于0的值,有描述符变化或超时时间到才返回。

        超时时间检测:如果规定时间内未完成函数功能,返回一个超时的信息,我们可以根据该信息设定相应需求;

返回值:  <0 出错            >0 表示有事件产生;

                如果设置了超时检测时间:&tv

                <0 出错            >0 表示有事件产生;      ==0 表示超时时间已到;        

结构体如下:                     

            struct timeval {

               long    tv_sec;         以秒为单位,指定等待时间

               long    tv_usec;        以毫秒为单位,指定等待时间

           };

void FD_CLR(int fd, fd_set *set);  //将set集合中的fd清除掉 

int  FD_ISSET(int fd, fd_set *set); //判断fd是否在set集合中产生了事件

void FD_SET(int fd, fd_set *set);  //将fd加入到集合中

void FD_ZERO(fd_set *set);          //清空集合

(3)Select特点:

Select特点:

1. 一个进程最多只能监听1024个文件描述符 (32位)   [64位为 2048]

2. select被唤醒之后要重新轮询(0-1023)一遍驱动,效率低(消耗CPU资源)

3. select每次会清空未响应的文件描述符,每次都需要拷贝用户空间的表到内核空间,效率低,开销较大

   (0~3G是用户态,3G~4G是内核态,两个状态来回切换  拷贝是非常耗时,耗资源的)

 (4)select机制: 

1. 头文件检测1024个文件描述符  0-1023

2. 在select中0~2存储标准输入、标准输出、标准出错    

3. 监测的最大文件描述个数为fd+1(如果fd = 3,则最大为 4) :  //因为从0开始的    

4. select只对置1的文件描述符感兴趣 ,假如事件产生,select检测时 , 产生的文件描述符会保持1,未产生事件的会置0; 

5. select每次轮询都会清空表(置零的清空)   //需要在select前备份临时表

练习1:

如何通过select实现 响应鼠标事件同时响应键盘事件?

代码:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>int main(int argc, char const *argv[])
{int fd = open("/dev/input/mouse0", O_RDONLY);if (fd < 0){perror("open is err:");return -1;}//1.创建表fd_set readfds;//2/清空表FD_ZERO(&readfds);//3.设置表FD_SET(0, &readfds);FD_SET(fd, &readfds);fd_set readfdcp = readfds;int maxfd = fd;char buf[128] = {0};while (1){//4.检测是否有相应select(maxfd + 1, &readfds, NULL, NULL, NULL);//5.检测哪一个文件描述符if (FD_ISSET(0, &readfds)){fgets(buf, sizeof(buf), stdin);if (buf[strlen(buf) - 1] == '\n')buf[strlen(buf) - 1] = '\0';printf("key: %s\n", buf);}if (FD_ISSET(fd, &readfds)){int ret = read(fd, buf, sizeof(buf));buf[ret] = '\0';printf("mouse: %s\n", buf);}readfds = readfdcp;}return 0;
}

 练习2:

select是文件描述符和下标一一对应,0只能对应0号文件描述符。因此只有最大的文件描述符关闭时,才--len。注意增加删除时是针对实际表,不是临时表。

使用select实现server的全双工

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <sys/select.h>int acceptfp;
int main(int argc, char const *argv[])
{char buf[128] = {0};//1.创建套接字,返回建立链接的文件描述符int sockfp = socket(AF_INET, SOCK_STREAM, 0);if (sockfp == -1){perror("socket is err");exit(0);}printf("%d\n", sockfp);//2.绑定ip和端口号struct sockaddr_in saddr, caddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[1]));saddr.sin_addr.s_addr = inet_addr("0.0.0.0");socklen_t len = sizeof(struct sockaddr_in);if (bind(sockfp, (struct sockaddr *)&saddr, sizeof(saddr)) < 0){perror("bind is err");exit(0);}//3.listen监听if (listen(sockfp, 5)){perror("liste err");exit(0);}printf("listen ok\n");//1.创建表fd_set readfds;//2/清空表FD_ZERO(&readfds);//3.设置表FD_SET(0, &readfds);FD_SET(sockfp, &readfds);fd_set readfdcp = readfds;int maxfd = sockfp;struct timeval st;while (1){readfds = readfdcp;//4.检测是否有响应st.tv_sec = 5;st.tv_usec = 0;int ret = select(maxfd + 1, &readfds, NULL, NULL, &st);if (ret < 0){perror("select err");return -1;}else if (ret == 0){printf("无响应\n");}//0响应,证明服务器要发送消息if (FD_ISSET(0, &readfds)){fgets(buf, sizeof(buf), stdin);if (buf[strlen(buf) - 1] == '\n')buf[strlen(buf) - 1] = '\0';for (int i = 4; i <= maxfd; ++i){send(i, buf, sizeof(buf), 0);}}//sockfp,监听套接字响应证明,有客户端要链接if (FD_ISSET(sockfp, &readfds)){acceptfp = accept(sockfp, (struct sockaddr *)&caddr, &len);if (acceptfp < 0){perror("acceptfp");exit(0);}printf("port:%d   ip:  %s\n", ntohs(caddr.sin_port), inet_ntoa(caddr.sin_addr));FD_SET(acceptfp, &readfdcp);if (acceptfp > maxfd)maxfd = acceptfp;}//检测客户端,检查是哪一个客户端发送的消息for (int i = 4; i <= maxfd; ++i){if (FD_ISSET(i, &readfds)){int recvbyte = recv(i, buf, sizeof(buf), 0);if (recvbyte < 0){perror("recv err");return -1;}else if (recvbyte == 0){printf("%d client is exit\n", i);close(i);FD_CLR(i, &readfdcp);if (i == maxfd)--maxfd;}else{printf("%d : %s\n", i, buf);}}}}return 0;
}

练习3:

使用select实现client的全双工

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>int main(int argc, const char *argv[])
{int sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd < 0){perror("socker is err:");return -1;}struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[1]));saddr.sin_addr.s_addr = inet_addr(argv[2]);if(connect(sockfd,(struct sockaddr *)&saddr,sizeof(saddr)) < 0){perror("connect is err:");return -1;}//1.创建表fd_set readfds,tempfds;//2.清空表FD_ZERO(&readfds);FD_ZERO(&tempfds);//3.添加文件描述符FD_SET(0,&readfds);FD_SET(sockfd,&readfds);int maxfd = sockfd;int ret;char buf[128];while(1){tempfds = readfds;//4.调select检测ret = select(maxfd+1,&tempfds,NULL,NULL,NULL);if(ret < 0){perror("select is err:");return -1;}if(FD_ISSET(0,&tempfds)){fgets(buf,sizeof(buf),stdin);if(buf[strlen(buf)-1] == '\n')buf[strlen(buf)-1] = '\0';send(sockfd,buf,sizeof(buf),0);}if(FD_ISSET(sockfd,&tempfds)){int recvbyte = recv(sockfd,buf,sizeof(buf),0);if(recvbyte < 0){perror("recv is err:");return -1;}printf("%s\n",buf);}}close(sockfd);return 0;
}

(5)select的超时时间检测:

超时检测的必要性:

1. 避免进程在没有数据时无限制的阻塞;

2. 规定时间未完成语句应有的功能,则会执行相关功能;

结构体如下:                     

            struct timeval {

               long    tv_sec;         以秒为单位,指定等待时间

               long    tv_usec;        以毫秒为单位,指定等待时间

           };

二.poll实现

 (1)poll流程

使用:  1.先创建结构体数组                                           struct pollfd fds[100];

          2.添加结构体成员的文件描述符以及触发方式   fds[0].fd = ?;fds[0].events = POLLIN 

          3.保存数组内最后一个有效元素的下标       

          4. 调用函数poll                                                  ret = poll(fds,nfds+1,-1);

          5.判断结构体内文件描述符是否触发事件          fds[i].revents == POLLIN

          6.根据不同的文件描述符触发不同事件 

(2)poll函数

声明:int poll(struct pollfd *fds, nfds_t nfds, int timeout);

头文件: #include<poll.h>

功能: 监视并等待多个文件描述符的属性变化

参数:

  1.struct pollfd *fds:   关心的文件描述符数组,大小自己定义

   若想检测的文件描述符较多,则建 立结构体数组struct pollfd fds[N]; 

           struct pollfd

           {

                  int fd;        //文件描述符

             short events;  //等待的事件触发条件----POLLIN读时间触发(大多数)

             short revents; //实际发生的事件(未产生事件: 0 ))

            }

    2.   nfds:        最大文件描述符个数

    3.  timeout: 超时检测 (毫秒级):1000 == 1s      

                          如果-1,阻塞          如果0,不阻塞

返回值:  <0 出错              >0 表示有事件产生;

              如果设置了超时检测时间:&tv

                <0 出错                >0 表示有事件产生;            ==0 表示超时时间已到;

(3)poll特点

1. 优化文件描述符个数的限制;

(根据poll函数第一个函数的参数来定,如果监听的事件为1个,则结构体数组容量为1,如果想监听100个,那么这个结构体数组的容量就为100,多少文件描述符由程序员自己来决定)

2. poll被唤醒之后需要重新轮询一遍驱动,效率比较低(消耗CPU)

3. poll不需重新构造文件描述符表(也不需清空表),只需要从用户空间向内核空间拷贝一次数据(效率相对比较高)

练习: 

使用poll实现server的全双工

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <sys/select.h>
#include <poll.h>int acceptfp;
int main(int argc, char const *argv[])
{char buf[128] = {0};//1.创建套接字,返回建立链接的文件描述符int sockfp = socket(AF_INET, SOCK_STREAM, 0);if (sockfp == -1){perror("socket is err");exit(0);}printf("%d\n", sockfp);//2.绑定ip和端口号struct sockaddr_in saddr, caddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[1]));saddr.sin_addr.s_addr = inet_addr("0.0.0.0");socklen_t len = sizeof(struct sockaddr_in);if (bind(sockfp, (struct sockaddr *)&saddr, sizeof(saddr)) < 0){perror("bind is err");exit(0);}//3.listen监听if (listen(sockfp, 5)){perror("liste err");exit(0);}printf("listen ok\n");//1.创建结构体数组struct pollfd fds[100];//2.添加文件描述符和触发方式fds[0].fd = 0;fds[0].events = POLLIN;fds[1].fd = sockfp;fds[1].events = POLLIN;int nfds = 1;int ret;while (1){//3.poll轮循检测ret = poll(fds, nfds + 1, 2000);if (ret < 0){perror("poll is err");return -1;}else if (ret == 0){printf("qeqweqe\n");continue;}//4. 判断哪一个文件描述符产生响应,并发布任务for (int i = 0; i <= nfds; ++i){if (fds[i].revents == POLLIN){if (fds[i].fd == 0){fgets(buf, sizeof(buf), stdin);if (buf[strlen(buf) - 1] == '\n')buf[strlen(buf) - 1] = '\0';//printf("发送信息:\n");for (int j = 2; j <= nfds; ++j){send(fds[j].fd, buf, sizeof(buf), 0);}}else if (fds[i].fd == sockfp){acceptfp = accept(sockfp, (struct sockaddr *)&caddr, &len);if (acceptfp < 0){perror("acceptfp");exit(0);}printf("port:%d   ip:  %s\n", ntohs(caddr.sin_port), inet_ntoa(caddr.sin_addr));fds[++nfds].fd = acceptfp;fds[nfds].events = POLLIN;}else{int recvbyte = recv(fds[i].fd, buf, sizeof(buf), 0);if (recvbyte < 0){perror("recv err");return -1;}else if (recvbyte == 0){printf("%d client is exit\n", i);close(fds[i].fd);//覆盖fds[i] = fds[nfds];//--i,--nfds后,最后一个循环不到--nfds, --i;}else{printf("%d : %s\n", i, buf);}}}}}return 0;
}

(4)poll超时时间检测

 timeout: 超时检测 (毫秒级):1000 == 1s      

                  如果-1,阻塞          如果0,不阻塞

三.epoll实现

(1)epoll流程:

Epoll的使用:

1.创建红黑树 和 就绪链表                                      int epfd = epoll_create(1);

2.添加文件描述符和事件信息到树上

    event.events = EPOLLIN|EPOLLET;

    event.data.fd = 0;

    epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event

3.阻塞等待事件的产生,一旦产生事件,则进行处理 

     int ret = epoll_wait(epfd,events,32,-1);

4.根据链中准备处理的文件描述符 进行处理

(2)epoll函数族 

epoll 要使用一组函数:       epoll_create 创建红黑树 和 就序链表

                                          epoll_ctl   添加文件描述符和事件到树上 / 从树上删除

                                          epoll_wait  等待事件产生

epoll_create 

创建红黑树以及链表

头文件:#include <sys/epoll.h>

声明:int epoll_create(int size);

功能:创建红黑树根节点(创建epoll实例) , 同时也会创建就绪链表

返回值:成功时返回一个实例(二叉树句柄),失败时返回-1。

epoll_ctl

控制epoll属性

声明: int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

功能:控制epoll属性,比如给红黑树添加节点

参数:  1. epfd:   epoll_create函数的返回句柄。//一个标识符

          2. op:表示动作类型,有三个宏:         

                EPOLL_CTL_ADD:注册新的fd到epfd中

                EPOLL_CTL_MOD:修改已注册fd的监听事件

                EPOLL_CTL_DEL:从epfd中删除一个fd

3. 要操作的文件描述符

4. 结构体信息:

        typedef union epoll_data {

                int fd; //要添加的文件描述符,只用这个

                uint32_t u32; typedef unsigned int

                uint64_t u64; typedef unsigned long int

        } epoll_data_t;

        struct epoll_event {

                uint32_t events; 事件

                epoll_data_t data; //共用体(看上面)

        };

           关于events事件:

            EPOLLIN:  表示对应文件描述符可读

            EPOLLOUT: 可写

            EPOLLPRI:有紧急数据可读;

            EPOLLERR:错误;

            EPOLLHUP:被挂断;

            EPOLLET:触发方式,边缘触发;(默认使用边缘触发)

            ET模式:表示状态的变化;

            NULL: 删除一个文件描述符使用,无事件

返回值:成功:0, 失败:-1

epoll_wait

等待事件产生

声明: int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

功能:等待事件产生

   内核会查找红黑树中有事件响应的文件描述符, 并将这些文件描述符放入就绪链表

    就绪链表中的内容, 执行epoll_wait会同时复制到第二个参数events

参数:   epfd:句柄;

           events:用来保存从就绪链表中响应事件的集合;(传出参数,定义结构体数组)

           maxevents:  表示每次在链表中拿取响应事件的个数;

           timeout:超时时间,毫秒,0立即返回  ,-1阻塞

返回值: 成功: 实际从链表中拿出的数目     失败时返回-1

(4)epoll特点

1.监听的最大的文件描述符没有个数限制(取决与你自己的系统 1GB - 10万个左右)

2.异步I/O,epoll当有事件产生被唤醒之后,文件描述符主动调用callback(回调函数)函数直接拿到唤醒的文件描述符,不需要轮询,效率高

3.epoll不需要重新构造文件描述符表,只需要从用户空间向内核空间拷贝一次数据即可.

(5)epoll机制

select,poll都属于 同步IO机制(轮询)

epoll属于异步IO机制(不轮询): 

Epoll处理高并发,百万级

  1. 红黑树: 是特殊的二叉树(每个节点带有属性),Epoll怎样能监听很多个呢?首先创建树的根节点,每个节点都是一个fd以结构体的形式存储(节点里面包含了一些属性,callback函数)
  2. 就绪链表: 当某一个文件描述符产生事件后,会自动调用callback函数,通过回调callback函数来找到链表对应的事件(读时间还是写事件)。

 

 练习:

epoll实现server

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <sys/select.h>
#include <poll.h>
#include <sys/epoll.h>int acceptfp;
int main(int argc, char const *argv[])
{char buf[128] = {0};//1.创建套接字,返回建立链接的文件描述符int sockfp = socket(AF_INET, SOCK_STREAM, 0);if (sockfp == -1){perror("socket is err");exit(0);}printf("%d\n", sockfp);//2.绑定ip和端口号struct sockaddr_in saddr, caddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[1]));saddr.sin_addr.s_addr = inet_addr("0.0.0.0");socklen_t len = sizeof(struct sockaddr_in);if (bind(sockfp, (struct sockaddr *)&saddr, sizeof(saddr)) < 0){perror("bind is err");exit(0);}//3.listen监听if (listen(sockfp, 5)){perror("liste err");exit(0);}printf("listen ok\n");//1.创建红黑树以及链表//树的跟节点/树的句柄int epfd = epoll_create(1);//2.上树struct epoll_event event;struct epoll_event events[32] ;event.events = EPOLLET | EPOLLIN;event.data.fd = 0;epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &event);event.data.fd = sockfp;epoll_ctl(epfd, EPOLL_CTL_ADD, sockfp, &event);while (1){//3.阻塞等待文件描述符产生事件int ret = epoll_wait(epfd, events, 32, -1);printf("asdsdfgdsf\n");if (ret < 0){perror("epoll err");return -1;}//4.根据文件描述符号,进行处理for (int i = 0; i < ret; ++i){if (events[i].data.fd == 0){fgets(buf, sizeof(buf), stdin);if (buf[strlen(buf) - 1] == '\n')buf[strlen(buf) - 1] = '\0';printf("发送信息:\n");//send(fds[j].fd, buf, sizeof(buf), 0);}else if (events[i].data.fd == sockfp){acceptfp = accept(sockfp, (struct sockaddr *)&caddr, &len);if (acceptfp < 0){perror("acceptfp");exit(0);}printf("port:%d   ip:  %s\n", ntohs(caddr.sin_port), inet_ntoa(caddr.sin_addr));//上树event.data.fd = acceptfp;epoll_ctl(epfd, EPOLL_CTL_ADD, acceptfp, &event);}else{int recvbyte = recv(events[i].data.fd, buf, sizeof(buf), 0);if (recvbyte < 0){perror("recv err");return -1;}else if (recvbyte == 0){printf("%d client is exit\n", events[i].data.fd);close(events[i].data.fd);//下树epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, NULL);}else{printf("%d : %s\n", events[i].data.fd, buf);}}}}return 0;
}

对比 

相关文章:

I/O多路复用三种实现

一.select 实现 &#xff08;1&#xff09;select流程 基本流程是&#xff1a; 1. 先构造一张有关文件描述符的表; fd_set readfds 2. 清空表 FD_ZERO() 3. 将你关心的文件描述符加入到这…...

DataInputStream数据读取 Vs ByteBuffer数据读取的巨大性能差距

背景&#xff1a; 今天在查找一个序列化和反序列化相关的问题时&#xff0c;意外发现使用DataInputStream读取和ByteBuffer读取之间性能相差巨大&#xff0c;本文就来记录下这两者在读取整数类型时的性能差异&#xff0c;以便在平时使用的过程中引起注意 DataInputStream数据…...

org.apache.flink.table.api.TableException: Sink does not exists

FlinkSQL_1.12_用DDL实现Kafka到MySQL的数据传输_实现按照条件进行过滤写入MySQL_flink从kafka拉取数据并过滤数据写入mysql_旧城里的阳光的博客-CSDN博客 参考这篇文章&#xff0c;写了kafka到mysql的代码例子&#xff0c;因为自己改了表结构&#xff0c;运行下面代码&#x…...

【多线程】CAS 详解

CAS 详解 一. 什么是 CAS二. CAS 的应用1. 实现原子类2. 实现自旋锁 三. CAS 的 ABA 问题四. 相关面试题 一. 什么是 CAS CAS: 全称Compare and swap&#xff0c;字面意思:”比较并交换“一个 CAS 涉及到以下操作&#xff1a; 我们假设内存中的原数据 V&#xff0c;旧的预期值…...

卷积神经网络实现咖啡豆分类 - P7

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制&#x1f680; 文章来源&#xff1a;K同学的学习圈子 目录 环境步骤环境设置包引用全局设备对象 数据准备查看图像的信息制作数据集 模型设…...

C++之默认与自定义构造函数问题(二百一十七)

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

Docker从认识到实践再到底层原理(五)|Docker镜像

前言 那么这里博主先安利一些干货满满的专栏了&#xff01; 首先是博主的高质量博客的汇总&#xff0c;这个专栏里面的博客&#xff0c;都是博主最最用心写的一部分&#xff0c;干货满满&#xff0c;希望对大家有帮助。 高质量博客汇总 然后就是博主最近最花时间的一个专栏…...

【Flowable】任务监听器(五)

前言 之前有需要使用到Flowable&#xff0c;鉴于网上的资料不是很多也不是很全也是捣鼓了半天&#xff0c;因此争取能在这里简单分享一下经验&#xff0c;帮助有需要的朋友&#xff0c;也非常欢迎大家指出不足的地方。 一、监听器 在Flowable中&#xff0c;我们可以使用监听…...

spring-kafka中ContainerProperties.AckMode详解

近期&#xff0c;我们线上遇到了一个性能问题&#xff0c;几乎快引起线上故障&#xff0c;后来仅仅是修改了一行代码&#xff0c;性能就提升了几十倍。一行代码几十倍&#xff0c;数据听起来很夸张&#xff0c;不过这是真实的数据&#xff0c;线上错误的配置的确有可能导致性能…...

【rpc】Dubbo和Zookeeper结合使用,它们的作用与联系(通俗易懂,一文理解)

目录 Dubbo是什么&#xff1f; 把系统模块变成分布式&#xff0c;有哪些好处&#xff0c;本来能在一台机子上运行&#xff0c;为什么还要远程调用 Zookeeper是什么&#xff1f; 它们进行配合使用时&#xff0c;之间的关系 服务注册 服务发现 动态地址管理 Dubbo是…...

ChatGPT的未来

随着人工智能的快速发展&#xff0c;ChatGPT作为一种自然语言生成模型&#xff0c;在各个领域都展现出了巨大的潜力。它不仅可以用于日常对话、创意助手和知识查询&#xff0c;还可以应用于教育、医疗、商业等各个领域&#xff0c;为人们带来更多便利和创新。 在教育领域&#…...

Pytorch模型转ONNX部署

开始以为会很困难&#xff0c;但是其实非常方便&#xff0c;下边分两步走&#xff1a;1. pytorch模型转onnx&#xff1b;2. 使用onnx进行inference 0. 准备工作 0.1 安装onnx 安装onnx和onnxruntime&#xff0c;onnx貌似是个环境。。倒是没有直接使用&#xff0c;onnxruntim…...

k8s优雅停服

在应用程序的整个生命周期中&#xff0c;正在运行的 pod 会由于多种原因而终止。在某些情况下&#xff0c;Kubernetes 会因用户输入&#xff08;例如更新或删除 Deployment 时&#xff09;而终止 pod。在其他情况下&#xff0c;Kubernetes 需要释放给定节点上的资源时会终止 po…...

面试题五:computed的使用

题记 大部分的工作中使用computed的频次很低的&#xff0c;所以今天拿出来一文对于computed进行详细的介绍&#xff0c;因为Vue的灵魂之一就是computed。 模板内的表达式非常便利&#xff0c;但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护…...

完美的分布式监控系统 Prometheus与优雅的开源可视化平台 Grafana

1、之间的关系 prometheus与grafana之间是相辅相成的关系。简而言之Grafana作为可视化的平台&#xff0c;平台的数据从Prometheus中取到来进行仪表盘的展示。而Prometheus这源源不断的给Grafana提供数据的支持。 Prometheus是一个开源的系统监控和报警系统&#xff0c;能够监…...

黑马JVM总结(九)

&#xff08;1&#xff09;StringTable_调优1 我们知道StringTable底层是一个哈希表&#xff0c;哈希表的性能是跟它的大小相关的&#xff0c;如果哈希表这个桶的个数比较多&#xff0c;元素相对分散&#xff0c;哈希碰撞的几率就会减少&#xff0c;查找的速度较快&#xff0c…...

如何使用 RunwayML 进行创意 AI 创作

标题&#xff1a;如何使用 RunwayML 进行创意 AI 创作 介绍 RunwayML 是一个基于浏览器的人工智能创作工具&#xff0c;可让用户使用各种 AI 功能来生成图像、视频、音乐、文字和其他创意内容。RunwayML 的功能包括&#xff1a; * 图像生成&#xff1a;使用生成式对抗网络 (…...

【css】能被4整除 css :class,判断一个数能否被另外一个数整除,余数

判断一个数能否被另外一个数整除 一个数能被4整除的表达式可以表示为&#xff1a;num%40&#xff0c;其中&#xff0c;num为待判断的数&#xff0c;% 为取模运算符&#xff0c;为等于运算符。这个表达式的意思是&#xff0c;如果num除以4的余数为0&#xff0c;则返回true&…...

ChatGPT与日本首相交流核废水事件-精准Prompt...

了解更多请点击&#xff1a;ChatGPT与日本首相交流核废水事件-精准Prompt...https://mp.weixin.qq.com/s?__bizMzg2NDY3NjY5NA&mid2247490070&idx1&snebdc608acd419bb3e71ca46acee04890&chksmce64e42ff9136d39743d16059e2c9509cc799a7b15e8f4d4f71caa25968554…...

关于 firefox 不能访问 http 的解决

情景&#xff1a; 我在虚拟机 192.168.x.111 上配置了 DNS 服务器&#xff0c;在 kali 上设置 192.168.x.111 为 DNS 服务器后&#xff0c;使用 firefox 地址栏搜索域名 www.xxx.com &#xff0c;访问在 192.168.x.111 搭建的网站&#xff0c;本来经 192.168.x.111 DNS 服务器解…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路

进入2025年以来&#xff0c;尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断&#xff0c;但全球市场热度依然高涨&#xff0c;入局者持续增加。 以国内市场为例&#xff0c;天眼查专业版数据显示&#xff0c;截至5月底&#xff0c;我国现存在业、存续状态的机器人相关企…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...

蓝桥杯 冶炼金属

原题目链接 &#x1f527; 冶炼金属转换率推测题解 &#x1f4dc; 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V&#xff0c;是一个正整数&#xff0c;表示每 V V V 个普通金属 O O O 可以冶炼出 …...

Leetcode33( 搜索旋转排序数组)

题目表述 整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 < k < nums.length&#xff09;上进行了 旋转&#xff0c;使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...

Spring Security 认证流程——补充

一、认证流程概述 Spring Security 的认证流程基于 过滤器链&#xff08;Filter Chain&#xff09;&#xff0c;核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤&#xff1a; 用户提交登录请求拦…...

SQL Server 触发器调用存储过程实现发送 HTTP 请求

文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...

Python网页自动化Selenium中文文档

1. 安装 1.1. 安装 Selenium Python bindings 提供了一个简单的API&#xff0c;让你使用Selenium WebDriver来编写功能/校验测试。 通过Selenium Python的API&#xff0c;你可以非常直观的使用Selenium WebDriver的所有功能。 Selenium Python bindings 使用非常简洁方便的A…...