WebServer -- 架构图 面试题(上)

news2024/9/23 5:34:31

目录

🎂前言

🌼流程图 && 架构图

1)什么是 WebServer

2)服务器基本框架

3)Reactor && Proactor 模式

4)同步 I/O 模拟Proactor模式(Linux)

5)主从Reactor模式

6)并发模式中的 同步读 && 异步读

7)半同步/半反应堆

8)半同步/半异步

9)解析报文(主从状态机 && 状态转移过程)

10)从状态机逻辑

11)响应报文

12)信号处理机制

13)日志系统

14)GET,POST 请求下页面跳转

15)架构图

16)源码目录解析

🚩面试题(上)

1)项目介绍

为什么要做 WebServer?

介绍下你的项目

2)线程池

手写线程池

线程同步机制有哪些

线程池中的工作线程是一直等待吗?

线程池中工作线程处理完一个任务后的状态是?

同时2000个客户端访问,线程数不多,如何及时响应?

一个请求占用线程很长事件,影响到了接下来的请求处理,如何解决

3)并发模型

服务器使用的并发模型是?

Reactor,Proactor,主从Reactor 模型的区别

为什么用 epoll,还有其他IO复用方式吗,区别是


🎂前言

本项目即将结束,手敲 14 篇万字博客,画了 20 多个流程图,整理了 40 多道相关八股

最后3篇博客:架构图 && 面试题(上),面试题(下) && 八股(上),八股(下)

🌼流程图 && 架构图

所有图我都画了一遍,然后,结合画的图,对照着看一遍源码的实现

(实际上,就是对照着流程图,回顾前面写过的博客,将每一个接口联系起来)

Excalidraw | Hand-drawn look & feel • Collaborative • Secure

哈哈哈,回看之前,自己亲手写的 11 篇博客,敲代码过程中的困惑也慢慢解开,融会贯通的感觉

1)什么是 WebServer

2)服务器基本框架

3)Reactor && Proactor 模式

4)同步 I/O 模拟Proactor模式(Linux)

5)主从Reactor模式

6)并发模式中的 同步读 && 异步读

7)半同步/半反应堆

8)半同步/半异步

9)解析报文(主从状态机 && 状态转移过程)

10)从状态机逻辑

11)响应报文

12)信号处理机制

13)日志系统

14)GET,POST 请求下页面跳转

15)架构图

16)源码目录解析

每个目录,我会结合(README 和 架构图)进行解析

总目录

总目录,我将它拆成上下两部分👇

  1. CGImysql: 处理  MySQL数据库  相关的 CGI 程序
  2. http: 处理  HTTP 请求和响应
  3. lock: 实现  锁机制 ,确保线程安全
  4. log: 实现  日志系统,记录服务器的运行状态
  5. root: 服务器的根目录,存放网站的  静态资源文件,用于构建用户界面
  6. test_pressure: 压力测试 
  7. threadpool: 实现  线程池  
  8. timer: 定时器
  9. LICENSE: 许可证文件,规定使用该项目的条款
  10. README.md: 项目的说明文档
  11. build.sh: 构建项目的脚本
  12. config.cpp 和 config.h: 存放  配置信息  的代码文件
  13. main.cpp: 主程序  入口文件
  14. makefile: 用于  编译链接项目  的 Makefile 文件
  15. webserver.cpp 和 webserver.h: 包含  Web服务器  的主要逻辑代码

http -- I/O处理单元

CGImysql -- 数据库连接

log -- 日志系统

lock -- 锁机制

timer -- 定时器处理非活动连接

CGImysql

校验 && 数据库连接池

数据库连接池

  • 单例模式,保证唯一
  • list 实现连接池
  • 连接池为静态大小
  • 互斥锁实现线程安全

校验

  • HTTP请求采用POST方式
  • 登录用户名和密码校验
  • 用户注册及多线程安全

http

http 连接处理类

根据状态转移,通过  主从状态机  封装了 http连接类

其中,主状态机在内部调用从状态机,从状态机将处理状态和数据传给主状态机

  • 客户端发出 http 连接请求
  • 从状态机读取数据,更新自身状态和接受数据,传给主状态机
  • 主状态机根据从状态机状态,更新自身状态,决定响应请求还是继续读取

lock

线程同步机制包装类

多线程同步,确保任一时刻只能有一个线程进入关键代码段

  • 信号量
  • 互斥锁
  • 条件变量

log

同步/异步日志系统

同步 / 异步日志系统主要涉及两个模块,一个是日志模块,一个是阻塞队列模块

加入阻塞队列模块的目的:实现  异步写入日志

  • 自定义阻塞队列
  • 单例模式创建日志
  • 同步日志
  • 异步日志
  • 实现按天,超行分类

root

界面跳转

对 html 中的 action 行为设置标志位,将 method 设置为 POST

  • 0 注册
  • 1 登录
  • 2 登陆检测
  • 3 注册检测
  • 5 请求图片
  • 6 请求视频
  • 7 关注我

test_pressure

服务器压力测试

 (1)

(2)

(3)

webbench -- 网站压测工具

  • 测试相同硬件不同服务的性能 && 不同硬件同一个服务的运行状况
  • 展示服务器的两项内容:每秒响应请求数 && 每秒传输数据量

threadpool

半同步 / 半反应堆线程池

使用一个工作队列,完全解除了主线程和工作线程的耦合关系:

主线程往工作队列插入任务,工作线程通过竞争来取得任务并执行它

  • 同步 IO 模拟 proactor 模式
  • 半同步 / 半反应堆
  • 线程池

timer

定时器处理非活动连接

由于非活跃连接占用连接资源(占着茅坑不拉屎),严重影响服务器性能

通过实现一个服务器定时器,处理这种非活跃连接,释放连接资源

利用 alarm 函数,周期性地出发 SIGALRM 信号

该信号处理函数利用管道通知主循环,执行链表上的定时任务

  • 统一事件源
  • 基于升序链表的定时器
  • 处理非活动连接

接着,重看一遍前面写过的博客(梳理逻辑,熟悉源码和接口<常见手撕>,为下一步搞定面试题做准备)

🚩面试题(上)

1)项目介绍

为什么要做 WebServer?

专业课学过C++,Linux,计网,Mysql等知识,所以想通过本项目巩固网络编程和Linux,将分散的知识串联起来,顺便入门服务器。

介绍下你的项目

  • 该项目通过C++,在Linux环境下开发
  • 使用webbench进行压力测试,达到上万并发量
  • 引入定时器,处理非活跃连接,及时释放连接资源,提升性能
  • alarm 函数周期性触发 SIGALRM 信号,使用管道通知主循环执行定时任务,实现统一事件源和基于升序链表的定时器
  • 采用线程池,半同步/半反应堆架构,解耦主线程和工作线程,提高并发处理能力
    (因为主线程和工作线程之前有个工作队列,所以两者间没有耦合性)
  • 引入同步/异步日志系统,实现异步写入日志功能
  • 通过主从状态机封装 http 连接类,实现对 HTTP 请求的处理
  • 实现数据库连接池,采用单例和互斥锁保证线程安全
  • CGImysql处理数据库连接,log模块记录日志,lock模块实现锁机制,timer模块处理非活动连接
  • 还支持处理大文件(包括视频文件和图片)
  • 同时支持Reactor和Proactor模式,ET / LT均支持

补充解释👇

利用CGI与MySQL提升网站性能(cgimysql)-数据运维技术 (dbs724.com)

2)线程池

详情请看👇

webserver 之 线程同步 && 线程池(半同步半反应堆)-CSDN博客

手写线程池

a. 定义

线程处理函数 worker() 和 执行任务函数 run() ---- 私有

只提供 构造,析构,添加任务 的公共接口

template<typename T>
class threadpol {
    public:
        // thread_num 线程数量
        // max_requests 请求数量(请求队列中 最多允许 && 等待处理)
        // connpool 数据库连接池 指针
        threadpool(connection_pool *connpool,
                   int thread_number = 8,
                   int max_request = 10000);
        ~threadpool();

        // 请求队列 插入任务请求
        bool append(T* request);

    private:
        // 工作线程运行的函数
        // 不断从工作队列取出任务 并执行
        static void *worker(void *arg); // 声明为 static 的原因,下面构造函数解释

        void run();

    private:
        int thread_number; // 线程数

        int m_max_requests; // 请求队列最大请求书

        pthread_t *m_threads; // 描述线程池的数组,大小 m_thread_num

        std::list<T *> m_workqueue; // 请求队列

        locker m_queuelocker; // 保护请求队列的互斥锁

        sem m_queuestat; // 是否有任务需要处理

        bool m_stop; // 结束线程
        
        connection_pool *m_connPool; // 数据库连接池
};

b. 构造

涉及线程池的 创建和回收

pthread_create() 将类的对象作为参数,传递给 静态函数 worker()

在静态函数引用这个独享,并调用其动态方法 run()

具体地,类对象传递时用 this 指针,传递给静态函数后,转换为线程池类,并调用私有 run()

template<typename T>
threadpool<T>::threadpool( connection_pool *connPool,
                           int thrad_number,
                           int max_requests) // 构造函数参数列表
                           :
                           m_thread_number(thread_number), // 线程数
                           m_max_requests(max_requests), // 最大请求数
                           m_stop(false), m_threads(NULL), // 结束线程 && 线程池数组
                           m_connPool(connPool) // 数据库连接池指针
{
    if (thread_number <= 0 || max_requests <= 0)
        throw std::exception();

    // 线程 id 初始化
    m_threads = new pthread_t[m_thread_number]; // 数组

    if (!m_threads)
        throw std::exception();

    for (int i = 0; i < thread_number; ++i) {
        // thread_create(线程标识符, 线程属性, worker()指针, worker()的参数)
        // 因为 worker指针,指向线程处理函数的地址,而且 worker() 作为类成员函数
        // 且指向threadpool对象的 this 指针,作为默认参数被传入 worker() 中
        // 此时会和 worker(void* arg) 的类型 void* 不匹配
        // 所以上面才将 worker() 声明为 static

        // 循环创建线程
        if (thread_create(m_threads + i, NULL, worker, this) != 0) {
            delete [] m_threads;
            throw std::exception();
        }

        // 线程分离后,不用单独回收工作线程,便于资源释放
        if (thread_detach(m_threads[i])) {
            delete [] m_threads;
            throw std::exception();
        }
    }

}

c. 析构

template<typename T>
threadpool<T>::~threadpool()
{
    delete[] m_threads;
}

d. append() 添加任务

list 容器 创建  请求队列

向队列添加任务时,通过  互斥锁  保证线程安全

添加完毕后,通过  信号量  提醒 “有任务要处理”

最后注意线程同步 ↓↓↓

使用了互斥锁 m_queuelocker.lock()m_queuelocker.unlock() 来保护对任务队列 m_workqueue 的访问,防止多个线程同时访问引起数据竞争。

使用信号量 m_queuestat.post() 来通知空闲线程有任务需要处理,避免了一个线程获取多个任务的情况,也确保每个任务都能得到及时处理

template<typename T>
bool threadpool<T>::append(T* request)
{
    m_queuelocker.lock(); // 关键代码段加锁

    // 根据硬件,预先设置请求队列最大值
    if (m_workqueue.size() > m_max_requests) {
        m_queuelocker.unlock();
        return false;
    }

    // 添加任务
    m_workqueue.push_back(request);
    m_queuelocker.unlock(); // 解锁

    // 信号量 提醒有任务处理
    m_queuestat.post();
    return true;
}

e. worker() 线程处理

内部访问私有函数 run(),完成线程处理要求

// 前面构造函数中 pthread_create 调用了 worker
template<typename T>
void* threadpool<T>::worker(void* arg)
{
    // 调用时 *arg 是 this
    // 所以该操作其实是获取 threadpool 对象地址

    // 参数强转线程池类,调用成员方法
    threadpool* pool = (threadpool*)arg;
    // 线程池每一个线程创建都会调用 run(),睡眠在队列中
    pool->run();
    return pool;
}

f. run() 执行任务

 工作线程 从 请求队列 取出某个任务进行处理,注意线程同步

// 线程池所有线程睡眠状态,等待请求队列新增任务
template<typename T>
void threadpool<T>::run()
{
    while (!m_stop) {
        // 信号量等待
        // m_queuestat 是否有任务需要处理
        m_queuestat.wait();

        // 被唤醒后先加互斥锁
        // m_workqueue 请求队列
        m_queuelocker.lock();

        if (m_workqueue.empty()) {
            m_queuelocker.unlock();
            continue;
        }

        // 请求队列取 第一个任务request
        // 任务从请求队列 删除
        T* request = m_workqueue.front();
        m_workqueue.pop_front();
        m_queuelocker.unlock();
        if (!request) continue;

        // 连接池取出一个 数据库连接
        request->mysql = m_connPool->GetConnection();

        // process(模板类中的方法,这里是 http 类) 进行处理
        request->process();

        // 数据库连接 放回连接池
        m_connPool->ReleaseConnection(request->mysql);
    }
}

线程同步机制有哪些

1)RAII

  • 之所以把 RAII 加到线程同步机制里,因为它可以用来管理 信号量,互斥量,条件变量等资源
  • RAII -- Resource Acquisition is Initialization,资源获取即初始化
  • 资源与对象的生命周期绑定,构造函数分配资源,析构函数释放资源
  • 比如智能指针

2)信号量(限制访问某个资源的线程数量

a. sem_init() 初始化 信号量
 
b. sem_destory() 销毁 信号量
 
c. sem_wait() 原子操作方式,信号量 -1;信号量 == 0,sem_wait() 阻塞
 
d. sem_post() 原子操作方式,信号量 +1;信号量 > 0,唤醒调用 sem_post()的线程

信号量就像是一个可以控制进程访问共享资源的门禁系统。这个门禁系统支持两种操作👇

  • 等待 (P) 操作:当一个进程试图访问共享资源时(比如想要通过门禁进入),它首先检查信号量的值。如果信号量大于 0(门禁开着),那么进程可以顺利通过,同时信号量减一(门禁关闭)。但是,如果信号量等于 0(门禁关着,有其他进程在使用资源),进程必须等待(挂起执行),不能继续执行直到有其他进程释放资源(信号量增加)
  • 信号 (V) 操作:当一个进程使用完共享资源时(比如离开了房间),它会执行信号操作。如果有其他进程因为等待资源而被挂起,那么这个信号会唤醒其中一个等待的进程,让其继续执行。否则,如果没有进程在等待资源,信号量会自增,表示资源又变得可用

3)条件变量(线程间通信;实现线程等待唤醒机制)

a. pthread_cond_init() 初始化
 
b. pthread_cond_destory() 销毁
 
c. pthread_cond_broadcast() 广播方式,唤醒所有等待目标条件变量的 线程
 
d. pthread_cond_wait() 等待目标条件变量
调用时,传入 mutex 参数 (加锁的互斥锁)
执行时,1) 调用线程 放入条件变量的 请求队列
       2) 互斥锁 mutex 解锁
       3) 函数返回 0 时,互斥锁再次被锁上
       4) 也就是说,函数内部,会有一次 解锁 和 加锁 操作

当一个线程调用 pthread_cond_wait() 等待目标条件变量时,它会将自己放入条件变量的请求队列中,并传入一个已经加锁的互斥锁(mutex参数)。接着,互斥锁会被解锁,让其他线程有机会操作共享资源。当函数返回 0 时,表示条件满足,此时会再次对互斥锁进行加锁操作,以确保线程安全地访问共享资源。因此,在 pthread_cond_wait() 函数内部会有一次解锁和加锁的操作,确保线程的正确执行顺序和共享资源的安全访问

4)互斥量(一次只允许一个线程访问资源)

即 互斥锁:保护关键代码段,确保 独占式 访问

a. 进入关键代码段 -- 获得互斥锁并加锁

b. 离开关键代码段 -- 唤醒等待该互斥锁的线程

a. pthread_mutex_init() 初始化互斥锁
 
b. pthread_mutex_destory() 销毁互斥锁
 
c. pthread_mutex_lock() 原子操作方式,给互斥锁,加锁
 
d. pthread_mutex_unlock() 原子操作方式,给互斥锁,解锁

线程池中的工作线程是一直等待吗?

  • 工作线程睡眠在工作队列上,当主线程将新任务添加到工作队列时,就会唤醒某个一直在等待的工作线程
  • 该工作线程从队列中取出任务并执行,其他工作线程则继续睡眠在工作队列上

线程池中工作线程处理完一个任务后的状态是?

  • 请求队列为空,则该线程进入线程池继续等待
  • 队列不为空,就和其他线程一起竞争任务

同时2000个客户端访问,线程数不多,如何及时响应?

 1,采用  线程池

  • 主线程与工作线程分离,使用线程池管理工作线程,避免主线程阻塞
  • 考虑扩大线程池容量
  • 线程池复用线程资源,避免频繁创建和销毁小城

2,采用  ET 模式和非阻塞 socket

  • ET模式和非阻塞socket,确保及时处理
  • 通过 epoll 的EPOLLONESHOT特性,降低可读,可写和异常事件被触发次数

3,采用  半同步/半异步并发模式

  • 通过 Reactor 或 Proactor 模式,满足并发量要求

4,采用  集群或者分布式

  • 通过分布式,将大任务拆分成小任务,各节点协同完成
  • 通过集群,增加服务器(节点)数量

什么是分布式,分布式和集群的区别又是什么?这一篇让你彻底明白!_什么叫分布式-CSDN博客

一个请求占用线程很长事件,影响到了接下来的请求处理,如何解决

  • 采取 异步非阻塞 模式,接收到新的请求后,不立即处理,而是安排一个以后的时间再发起请求,并且继续执行当前请求
  • 采取 超时设置,对每个请求设置合理的超时时间,如果处理时间超过给定阈值,则取消该请求 或 返回超时错误信息,及时释放线程资源
  • 采取 断点续传,对任务进行分段处理,某个处理阶段完成后暂停任务,等待下一次触发继续处理
  • 任务分片,长时间任务拆分成多个小任务,每个小任务完成后释放线程资源

补充理解

深入理解同步阻塞、同步非阻塞、异步阻塞、异步非阻塞_同步阻塞 同步非阻塞 异步阻塞 异步非阻塞-CSDN博客

3)并发模型

服务器使用的并发模型是?

采用 半同步半反应堆 作为并发模型

Proactor 事件处理模式为例

  1. 主线程充当异步线程,负责监听所有 socket 上的时间和处理 I/O 操作
  2. 新请求到达时,主线程接收连接 socket,并注册读写时间到 epoll 内核事件表中
  3. 如果连接 socket 上有读写事件发生,主线程从 socket 上接收数据,并将数据封装成请求对象插入请求队列
  4. 所有工作线程睡眠在请求队列上,当有任务到来时,通过竞争(互斥锁)获得任务接管权
  5. 工作线程仅负责业务逻辑,比如处理客户请求;主线程与内核配合实现底层的异步 I/O 操作

总的来说

采用半同步半反应堆并发模型的 Proactor 事件处理模式,主线程负责底层 I/O 操作和事件监听,工作线程专注于业务逻辑处理,通过异步 I/O 实现高效并发处理

Reactor,Proactor,主从Reactor 模型的区别

先通过Java了解下 主从Reactor👇

Reactor(主从)原理详解与实现_主从reactor-CSDN博客

回答: 

1,Reactor(事件来了,我通知你,你来处理)(我 -- 操作系统内核;事件 -- 新连接)

  • 采用同步 I/O,负责监听文件描述符上是否有事件发生
  • 主线程(I/O 处理单元)负责事件感知和通知工作线程(同步IO向应用程序通知的是IO就绪事件),将读写事件放入请求队列,并由工作线程完成实际的数据读写,接收新连接 和 处理客户请求
  • 应用进程需要主动调用 read / write 方法,进行数据的读取和写入,处理过程是同步的
  • 比如,快递员在楼下通知你快递送达,需要你自己下楼拿快递

2,Proactor(事件来了,我处理完,再通知你)

  • 采用异步 I/O,只负责发起 I/O 操作,真正的 I/O 实现由操作系统处理
  • 主线程和操作系统负责处理读写数据,接收新连接等 IO 操作,工作线程仅负责业务逻辑,如处理客户请求
  • 应用进程无需主动发起读写操作,操作系统完成读写后,会通知应用进程直接处理数据(异步IO向应用程序通知的是IO完成事件
  • 比如,快递员将快递送达你家门口后,再通知你

3,主从Reactor模式

  • 主反应堆线程,负责分发连接建立事件,已连接套接字上的 IO 事件,交给子反应堆线程处理
  • 子反应堆线程数量,可以根据CPU核数来设置,负责具体的 I/O 事件处理
  • 主反应堆线程,只负责调用 accept 获取已连接套接字,并将其分配给响应的子反应堆线程
  • 结合了 Reactor 和 Proactor 的特点

概括地说,Reactor模式和Proactor模式都是基于事件分发的网络编程模式,区别在于 I/O 事件处理的方式

Reactor基于待完成的 I/O 事件,而Proactor基于已完成的 I/O 事件,主从Reactor结合了两者的优点

为什么用 epoll,还有其他IO复用方式吗,区别是

常用的是 select,poll,epoll,当然,这里会补充下对 io_uring 的说明

选择 epoll 从两点出发:

  1. 性能:
    1)在文件描述符数量较多且活跃度不一的情况下,epoll 能提升性能
    2)因为 epoll 将文件描述符维护在内核态,每次添加文件描述符,只需要执行一个系统调用
    3)而且能够直接返回触发事件的文件描述符,避免了遍历整个文件描述符集合的性能损耗
  2. 数据结构:
    1)select 使用线性表创建文件描述符集合,而且上限 1024
    2)poll 使用链表
    3)epoll 底层采用红黑树来构建,并维护一个 ready list,能够在 epoll_wait() 调用时,只观察已就绪事件
    4)epoll 支持 LT 和 ET 两种工作模式,而 select 和 poll 只能工作在相对低效的 LT 下

区别是:

  • select, poll 都需要将文件描述符集合从用户态拷贝到内核态,而epoll只用拷贝需要修改的文件描述符,避免了集体拷贝的开销
  • select,poll 最大开销来自内核判断是否有文件描述符就绪,需要遍历整个文件描述符集合,而 epoll 直接返回触发事件的文件描述符
  • select, poll, epoll 都是同步 I/O,而 io_uring 是异步 I/O,它通过用户和内核之间的共享内存映射来避免数据拷贝,减少CPU开销,还支持事件批处理,降低系统调用次数和上下文切换的开销

下面介绍下 io_uring:

  1. 零拷贝:io_uring 通过共享内存映射来避免数据拷贝,将用户空间和内核空间之间的数据传输最小化
  2. 批处理:它还支持事件批处理,即一次性可以提交多个 I/O 请求给内核,减少系统调用和上下文切换的开销,提高吞吐量
  3. Ring Buffer 机制:io_uring 使用环形缓冲区(ring buffer),作为用户和内核之间的通信机制。用户将 IO 请求放入Ring Buffer,内核会异步处理这些请求,并将结果返回到 Ring Buffer,用户再从 Ring Buffer 读取
  4. 高效的事件通知机制:使用 IO Completion Event(IO完成事件)机制,通过事件通知的方式告知用户空间 IO 操作的完成情况,避免用户频繁的轮询内核,提高响应速度

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

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

相关文章

小白刷题CTF show web方向

web01 右键查看源代码&#xff0c;再使用在线解密&#xff0c;就可以得出答案了 web02 sql注入 admin or 11 或者 1 or 11可以登录查询几个字段&#xff1a;1 or 11 order by 3 # 使用此语句&#xff0c;判断列数。 order by 3不会出错&#xff0c;但是order by 4就没有显示…

上传文件携带参数总是deubg不进去

const { data } await createVerificationMaterialApi({file: info.file,name: file,filename: info.file.name,data: { ids },})//这样传参数&#xff0c;网络里看发的请求会是如下样子&#xff0c;这样debug不到代码里正确方法 const { data } await createVerificationMat…

这下爽了,全是特殊版实用软件,功能强大还免费

闲话少说&#xff0c;直接上狠货。 1、我的ABC软件工具箱 简洁而不失强大&#xff0c;我的ABC软件工具箱是您批量处理办公任务的得力小助手。完全免费&#xff0c;界面清新无广告&#xff0c;让您轻松开启高效办公之旅。 面对日常办公中繁多的文件处理需求&#xff0c;如内容…

1.Datax数据同步之Windows下,mysql数据同步至另一个mysql数据库

目录 前言步骤操作大纲步骤明细其他问题 前言 Datax是什么&#xff1f; DataX 是阿里巴巴集团内被广泛使用的离线数据同步工具/平台&#xff0c;实现包括 MySQL、SQL Server、Oracle、PostgreSQL、HDFS、Hive、HBase、OTS、ODPS 等各种异构数据源之间高效的数据同步功能。准备…

IO学习--02

标准IO由ANSI C库说明&#xff0c;在很多系统都实现了标准IO库。标准IO库处理很多细节&#xff0c;如缓冲的分配、优化长度执行IO等&#xff0c;使得用户不需要考虑选择合适的长度。标准IO是在系统调用函数构建的&#xff0c;便于用户使用。 标准IO的所有操作都是围绕流&#x…

c语言经典测试题12

1.题1 float f[10]; // 假设这里有对f进行初始化的代码 for(int i 0; i < 10;) { if(f[i] 0) break; } 上述代码有那些缺陷&#xff08;&#xff09; A: for(int i 0; i < 10;)这一行写错了 B: f是float型数据直接做相等判断有风险 C: f[i]应该是f[i] D: 没有缺…

LLM PreTraining from scratch -- 大模型从头开始预训练指北

最近做了一些大模型训练相关的训练相关的技术储备,在内部平台上完成了多机多卡的llm 预训练的尝试,具体的过程大致如下: 数据准备: 大语言模型的训练依赖于与之匹配的语料数据,在开源社区有一群人在自发的整理高质量的语料数据,可以通过 以下的一些链接获取 liwu/MNBVC…

Java多线程——对象的原子更新

目录 引出对象原子更新AtomicReferenceAtomicLongFieldUpdaterABA问题 创建线程有几种方式&#xff1f;方式1&#xff1a;继承Thread创建线程方式2&#xff1a;通过Runnable方式3&#xff1a;通过Callable创建线程方式4&#xff1a;通过线程池概述ThreadPoolExecutor API代码实…

在VMvare中虚拟机安装centos7和初始设置

下载镜像 阿里云的镜像站&#xff1a;https://mirrors.aliyun.com/centos/7/isos/x86_64/ 创建虚拟机过程 虚拟机创建过程比较简单&#xff0c;以下在VMvare16中进行安装 点击左上角&#xff0c;文件-新建虚拟机&#xff1a; 选择典型 选择刚刚下载好的镜像 输入虚拟机…

#QT(本地音乐播放器)

1.IDE&#xff1a;QTCreator 2.实验&#xff1a;之前做的音乐播放器只做了一个界面&#xff0c;是因为跟的课程发现到后面需要付费&#xff0c;并且WINGW6.2.0运行QMediaPlayer时无法运行&#xff0c;会崩溃&#xff0c;现在退一步用WINGW5.12.2做一个本地音乐播放器 3.记录&am…

012集——显示高考天数倒计时——vba实现

以下代码实现高考倒计时&#xff1a; Sub 高考倒计时() 高考日期 CDate("06,07," & Year(Date)) If Date > 高考日期 Then高考日期 CDate("06-07-" & Year(Date) 1) End If 年月日 Year(Date) & "年" & Month(Date) &am…

鲜花销售小程序|基于微信小程序的鲜花销售系统设计与实现(源码+数据库+文档)

鲜花销售小程序目录 目录 基于微信小程序的鲜花销售系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1前台功能模块 2、后台功能模块 1、管理员功能模块 四、数据库设计 1、实体ER图 2、具体的表设计如下所示&#xff1a; 五、核心代码 六、论文参考 七、…

LabVIEW多表位数字温湿度计图像识别系统

LabVIEW多表位数字温湿度计图像识别系统 解决数字温湿度计校准过程中存在的大量需求和长时间校准问题&#xff0c;通过LabVIEW开发平台设计了一套适用于20多个表位的数字温度计图像识别系统。该系统能够通过图像采集、提取和处理&#xff0c;进行字符训练&#xff0c;从而实现…

LiveGBS流媒体平台GB/T28181功能-海康摄像头国标语音对讲大华摄像头国标语音对讲GB28181语音对讲需要的设备及服务准备

LiveGBS海康摄像头国标语音对讲大华摄像头国标语音对讲GB28181语音对讲需要的设备及服务准备 1、背景2、准备2.1、服务端必备条件&#xff08;注意&#xff09;2.2、准备语音对讲设备2.2.1、 大华摄像机2.2.1.1、 配置接入示例2.2.1.2、 配置音频通道编号 2.2.2、 海康摄像机2.…

现货黄金交易网上有用的交易技巧

在不同的现货黄金交易网上&#xff0c;经常有投资者分享交易技巧。由于在网上发文没什么限制&#xff0c;所以这些交易技巧都是泥沙俱下&#xff0c;质量良莠不齐。不过也有一些是有用的&#xff0c;下面我们就来介绍一下现货黄金交易网上那些有用的交易技巧。 培养防守意识。什…

swiftUI中的可变属性和封装

swiftUI的可变属性 关于swift中的属性&#xff0c;声明常量使用let &#xff0c; 声明变量使用var 如果需要在content中更改视图变化那么就需要在 var前面加上state 。 通过挂载到state列表 &#xff0c;从而让xcode找到对应的改变的值 例子&#xff1a; import SwiftUIstruc…

Java后端八股------消息中间件篇

自动确认没收到&#xff0c;实现重复消费问题&#xff0c;可以用业务唯一标识来确定业务是否被消费。 TTL也就是超时时间&#xff0c;一般去dead letter的时间为min(消息的ttl,queue的ttl)。 acksall设置是最安全的&#xff0c;但是效率太低了&#xff0c;实际的生…

App自动化测试环境搭建(详细版)

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 只做记录和注意点&#xff0c;详细内容不做解释 环境&#xff1a;winappium夜神模拟器python 需…

企业电子招投标系统源码-从源码到实践:深入了解鸿鹄电子招投标系统与电子招投标

在数字化采购领域&#xff0c;企业需要一个高效、透明和规范的管理系统。通过采用Spring Cloud、Spring Boot2、Mybatis等先进技术&#xff0c;我们打造了全过程数字化采购管理平台。该平台具备内外协同的能力&#xff0c;通过待办消息、招标公告、中标公告和信息发布等功能模块…

echarts绘制雷达图

<template><div><div>【云端报警风险】</div><div ref"target" class"w-full h-full" stylewidth&#xff1a;200px;height:300px></div></div> </template><script setup> import { ref, onMounte…