HTTPServer改进思路1

Nginx源码思考项目改进 

架构模式

  • 事件驱动架构(EDA)用于处理大量并发连接和IO操作
    • 优点:高效处理大量并发请求,减少线程切换和阻塞调用
    • 技术实现:直接使用EPOLL,参考Node.js的http服务器
  • 网络通信
    • 协议:HTTP2
      • 本身是改进的HTTP1.1,保持与HTTP的高兼容性(gRPC虽然可以提供更高效的传输效率,但是与项目的主要功能不符,所以暂时不用)
      • HTTP2,在快速加载静态和动态库的内容中适用,可以提高速度
      • 主要特性
        • 多路复用:在同一连接中并行处理多个请求和响应,减少了延迟。
        • 服务器推送:允许服务器未经请求即推送资源,提高页面加载速度。
        • 头部压缩:通过HPACK压缩协议减少了请求和响应头的大小。
      • 尝试使用nghttp2的库来支持HTTP2
    • gRPC:基于HTTP/2的高效RPC协议,适用于服务间通信。
    • WebSocket:用于实时通信的全双工协议。
  • 并发处理
    • 事件驱动模型
      • Reactor模式:主线程负责监听事件并分发给工作线程处理。适用于高并发、低延迟场景。(大量短请求)
      • Proactor模式:主线程负责完成事件处理,工作线程处理业务逻辑。适用于需要高并发和高吞吐量的场景。
    • 线程池
      • 使用线程池处理并发请求,避免频繁创建和销毁线程的开销。
      • 配置合理的线程池大小,避免过多或过少的线程影响性能。
    • 协程
      • 使用协程实现轻量级并发处理,提高系统资源利用率。协程相比于线程,开销更小,切换更快。
  • 资源管理
    • 内存管理
      • 使用高效的内存分配器,如tcmalloc、jemalloc,减少内存碎片和分配开销。
      • 避免内存泄漏,使用智能指针(如C++的std::shared_ptrstd::unique_ptr)管理内存。
    • 文件描述符管理
      • 合理分配和管理文件描述符,避免资源泄漏。
    • 限流
      • 实现限流策略,防止高并发请求压垮系统。可以使用漏桶算法或令牌桶算法。
  • 缓存
    • 本地缓存
      • 在服务器内存中缓存频繁访问的数据,减少数据库访问次数。
    • 分布式缓存
      • 对于需要高一致性的场景,使用一致性哈希算法管理缓存节点

HTTP请求模块阅读后思考

主要目标是在云服务器上设计一个高性能HTTP服务器,从而实现HTTP请求的高效处理。

改进方向

  • 非阻塞IO和事件驱动模型
    • 使用EPOLL以及reactor去完成高效的I多路复用机制
    • 尝试改进方向
      • EPOLL来处理并发连接
      • 使用非阻塞的IO操作,避免单个连接阻塞整个服务器
    • 内存池管理
      • 使用内存池进行内存管理,减少频繁内存分配和释放内存操作,提高内存利用率,减少内存碎片
      • 改进方向
        • 实现简单的内存管理器,预分配大内存,然后从中分配小内存
        • 请求结束后统一释放内存
    • 请求处理和解析
      • 优化存储的数据结构
    • 反向代理和负载均衡
      • 将请求转发到后端服务器,同时将响应返回给客户端。
      • 改进方向
        • 使用功能HTTP客户端库或者服务器完成HTTP请求转发
        • 尝试使用简单的轮询、加权轮询或者哈希算法实现负载均衡
    • 压缩和缓存
      • 对HTTP响应进行gzip压缩,减少传输数据量。实现缓存功能,缓存后端服务器的响应,减少后端服务器负载,提高响应速度。
      • 改进方向
        • 使用zlib库进行gzip压缩。
        • 使用哈希表或LRU缓存算法实现缓存。
    • 限流和访问控制
      • 限制客户端的请求速率,防止恶意请求或流量攻击。通过IP地址或其他条件进行访问控制,限制特定客户端的访问权限
      • 改进方向
        • 使用令牌桶或漏桶算法实现限流。
        • 使用IP白名单或黑名单进行访问控制

修改服务器架构 

EPOLL事件驱动在HTTP服务器架构下,HTTP请求和HTTP响应分别对应EPOLL的什么状态。

  • EPOLLIN (Readable): 表示有新的数据可读。对于一个HTTP服务器,当一个新的HTTP请求到达时,socket变为可读状态,触发EPOLLIN事件。你需要监听这个事件来读取客户端发送的HTTP请求数据。

  • EPOLLOUT (Writable): 表示可以向socket写入数据。当你需要向客户端发送HTTP响应时,通常会监听这个事件。

  • EPOLLET (Edge Triggered): EPOLL的边缘触发模式。这个模式下,事件只在状态变化时通知,所以需要非阻塞I/O和循环读取/写入数据,直到数据全部处理完毕。对于高性能服务器,这种模式更高效。

事件类型和socket之间的关系分析

  • ADD (EPOLL_CTL_ADD): 当你第一次监听一个socket时,使用EPOLL_CTL_ADD事件类型,将socket添加到EPOLL实例中,并指定你要监听的事件类型(通常是EPOLLIN)。

  • MOD (EPOLL_CTL_MOD): 当你已经在监听一个socket,但想修改其监听的事件类型时,使用EPOLL_CTL_MOD事件类型。例如,当你读取完HTTP请求数据后,想监听EPOLLOUT事件以便发送响应,就可以使用EPOLL_CTL_MOD修改监听事件类型。

  • DEL (EPOLL_CTL_DEL): 当你不再需要监听某个socket时,使用EPOLL_CTL_DEL事件类型将其从EPOLL实例中删除。

HTTP请求后,EPOLL具体实现步骤分析

  • 添加socket到EPOLL实例中: 使用epoll_ctl函数和EPOLL_CTL_ADD事件类型,将监听的socket添加到EPOLL实例中,并指定要监听EPOLLIN事件。
  • 读取请求数据: 当EPOLL检测到EPOLLIN事件时,读取HTTP请求数据。
  • 修改监听事件类型: 如果需要发送响应,可以使用EPOLL_CTL_MOD修改监听事件类型为EPOLLOUT。
  • 发送响应数据: 当EPOLL检测到EPOLLOUT事件时,发送HTTP响应数据。
//简化EPOLL模型#include <sys/epoll.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>#define MAX_EVENTS 10
#define READ_BUFFER_SIZE 1024void handle_connection(int client_fd) {char buffer[READ_BUFFER_SIZE];int bytes_read = read(client_fd, buffer, sizeof(buffer) - 1);if (bytes_read > 0) {buffer[bytes_read] = '\0'; // Null-terminate the stringprintf("Received request:\n%s\n", buffer);// Process the HTTP request and generate a response here.// For simplicity, we'll just send a basic HTTP response.const char *response = "HTTP/1.1 200 OK\r\nContent-Length: 13\r\n\r\nHello, World!";write(client_fd, response, strlen(response));} else if (bytes_read == 0) {// Client closed the connectionclose(client_fd);} else {// Read errorperror("read");close(client_fd);}
}int main() {int listen_fd = socket(AF_INET, SOCK_STREAM, 0); // Create a listening socket// Bind and listen steps are omitted for brevity...int epoll_fd = epoll_create1(0);if (epoll_fd == -1) {perror("epoll_create1");exit(EXIT_FAILURE);}struct epoll_event event;struct epoll_event events[MAX_EVENTS];event.data.fd = listen_fd;event.events = EPOLLIN;if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &event) == -1) {perror("epoll_ctl: listen_fd");exit(EXIT_FAILURE);}while (1) {int n = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);for (int i = 0; i < n; i++) {if (events[i].events & EPOLLIN) {if (events[i].data.fd == listen_fd) {// Handle new connectionint client_fd = accept(listen_fd, NULL, NULL);if (client_fd == -1) {perror("accept");continue;}// Add new client socket to EPOLL instanceevent.data.fd = client_fd;event.events = EPOLLIN;if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event) == -1) {perror("epoll_ctl: client_fd");close(client_fd);}} else {// Handle data from a connected clienthandle_connection(events[i].data.fd);// Optionally, modify the socket to listen for EPOLLOUT if needed}}}}close(listen_fd);close(epoll_fd);return 0;
}

项目设计分析与部分改进

TcpServer.hpp

  • getinstance()

    • 作用:实现单例模式的方法,用于确保TcpServer类的实例在程序生命周期内只被创建一次
    • 单例模式的目的:一个类只有一个实例,并提供一个全局访问点。
    • 静态互斥锁:线程锁的Lock确保它只可以被初始化一次,所以声明为静态,并在所有的getinstance的调用中共享。
    • 双重检查是否上锁:外部检查避免了在单例实例已经创建获取锁。内部检查确保了线程安全。
    • 类外初始化:类外初始化静态成员svr
    • 私有化构造函数:构造函数私有化,防止直接实例化类

 

 改进思路

 使用C++11的静态局部变量初始化特性,简化代码的同时保证线程安全

  • std::once_flag std::call_once: 使用 std::once_flagstd::call_once 确保 TcpServer 实例只被初始化一次,并且是线程安全的。
  • 静态局部变量: svr 作为静态局部变量,只会被初始化一次,确保单例的唯一性。
  • 构造函数私有化: 将构造函数设为私有,防止类外部直接创建实例。
  • 禁止拷贝和赋值: 删除拷贝构造函数和赋值操作符,防止复制单例实例。
class TcpServer
{
private:int port;int listen_sock;// static TcpServer*svr;static std::unique_ptr<TcpServer> svr; 
private:TcpServer(int _port):port(_port),listen_sock(-1){}TcpServer(const TcpServer&s) = delete;TcpServer&operator = (const TcpServer&) = delete;
public://单例模式static TcpServer*getinstance(int port){// static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;// if(nullptr == svr)// {//     pthread_mutex_lock(&lock);//     if(nullptr == svr)//     {//         svr = new TcpServer(port);//         svr->InitServer();//     }//     pthread_mutex_unlock(&lock);// }// return svr;static std::once_flag initFlag;std::call_once(initFlag,[&](){svr.reset(new TcpServer(port));svr->InitServer();    });return svr.get();}void InitServer(){......}
};// TcpServer*TcpServer::svr = nullptr;
std::unique_ptr<TcpServer> TcpServer::svr = nullptr;

 HttpServer.hpp

InitServer()

  • 忽略SIGPIPE信号,防止写入到已经关闭的套接字时导致服务器崩溃
    • unix系统编程中,SIGPIPE信号通常与管道和套接字的写操作有关,当进程试图向一个已经关闭的管道或者套接字写入数据的时候,系统会向进程发送SIGPIPE信号。如果进程没有处理这个信号,默认行为则是终止进程。所以为了避免这种情况的发生则忽略SIGPIPE信号

 Loop()

  • accept接受新连接,tsvr->sock返回服务器监听的套接字描述符,peer用于保存客户端的地址信息
  • 创建任务并加入线程池
  • Loop函数是服务器的核心,它不断监听客户端连接请求,当接收到新连接的时候,将其封装成任务并提交给线程池处理。通过线程池来提高服务器的并发处理能力,同事避免频繁创建和销毁线程的开销

 

整体代码实现 

#pragma once #include<iostream>
#include<pthread.h>
#include<signal.h>
#include"TcpServer.hpp"
#include"log.hpp"
#include"Task.hpp"
#include"ThreadPoll.hpp"#define PORT 8888class HttpServer
{
private:int port;bool stop; //标记服务状态
public:HttpServer(int _port=PORT):port(_port),stop(false){}void InitServer(){//避免写入时,server崩溃,忽略SIGPIPE信号signal(SIGPIPE,SIG_IGN);LOG(INFO,"server initialized");}//启动服务void Loop(){TcpServer*tsvr = TcpServer::getinstance(port);LOG(INFO,"Loop begin")while(!stop){struct sockaddr_in peer;socklen_t len = sizeof(peer);int sock = accept(tsvr->Sock(),(struct sockaddr*)&peer,&len);if(sock<0){continue;}LOG(INFO,"Get a new link");//构建任务Task task(sock);//向线程池中传入任务zThreadPoll::getinstance()->PushTask(task);}}~HttpServer(){}
};

 ThreadPoll.hpp

成员变量分析

  • num: 线程池中线程的数量。
  • stop: 标记线程池是否停止。
  • task_queue: 存储待处理任务的队列。
  • lock: 保护任务队列的互斥量。
  • cond: 用于线程间同步的条件变量。

单例模式设计

  •  single_instance:静态指针,指向唯一的线程池实例
  • getinstance():使用双重检查锁定模式确保线程池实例在多线程环境下安全初始化

 

任务管理模块

  •  pushTask:将任务添加到任务队列,并唤醒一个线程
  • popTask:从任务队列中取出任务

 

 InitThreadPoll()

线程池初始化,创建num个线程,并启动它们执行ThreadRoutine函数。线程池层创建多个工作线程,每个线程在后台等待并处理任务队列中的任务。通过线程池提高服务器的并发处理能力,避免了频繁创建和销毁线程的开销。

 

 ThreadRoutine(void* args)

每个线程在任务队列为空时等待任务,一旦有任务则取出并进行处理。无限循环的从任务队列中获取任务进行处理,通过加锁确保访问任务队列是线程安全的,while循环检查任务队列是否为空,等待任务队列的到来,防止虚假唤醒。

 

改进思路

  •  添加停止机制:添加stopAll方法,安全的停止所有线程
  • 条件变量广播:使用pthread_cond_broadcast唤醒所有线程,以便在停止线程池的时候所有线程都能够及时退出
  • 改进线程池的单例模式:使用C++11中std::call_once和std::once_flag确保线程池实例的线程安全初始化。同时使用std::unique_ptr管理单例实例的生命周期
    • 线程安全:std::call_once和std::once_flag确保初始化操作线程安全
    • 自动资源管理:使用std::unique_ptr管理单例实例,确保资源在程序结束的时候自动释放,避免内存泄漏

 

 ThreadPoll单例模式实现过程分析

  • std::call_once(initInstanceFlag, []() { ... }); 确保 lambda 表达式只会被调用一次。
  • single_instance.reset(new ThreadPoll()); 创建一个新的 ThreadPoll 实例,并将其指针管理权交给 single_instance
  • single_instance->InitThreadPoll(); 初始化线程池。
  • return single_instance.get(); 返回 ThreadPoll 实例的指针。

当其他线程同时调用getinstance的时候,由于std::call_once的保证,lambda表达式只会在第一个线程调用时执行一次,后续的调用都不会重复执行对象的创建和初始化,所有的线程都会返回同一个ThreadPoll实例

 std::call_once函数

  • C++11引入的一个标准库函数,用于确保某个操作只被执行一次,即使有多个线程同时进行该操作。与std::once_flag配合使用,能够方便的实现线程安全的初始化。
  • 参数分析
    • flag: 一个 std::once_flag 对象,用来保证所调用的函数只执行一次。
    • f: 要执行的函数,可以是函数指针、函数对象或 lambda 表达式。
    • args...: 要传递给函数 f 的参数。

single_instance.reset(new ThreadPoll())

  • sigle_instance是一个unique_ptr对象,而reset是unique_ptr的成员函数
  • reset:成员函数用于重置智能指针,释放其当前持有的对象,并让其持有新的对象

 

线程池的优缺点 

优点

  • 提高并发性能
    • 减少线程创建和销毁的内存开销:线程池中的线程是在初始化的时候创建的,而不是等待每次任务到来的时候才创建的,从而减少频繁创建和销毁线程所带来的系统开销。
    • 提高资源利用效率:通过限制最大线程数量,从而避免系统资源被过度消耗,从而防止因为创建过度线程导致的性能下降。
  • 简化编程
    • 代码逻辑清晰:任务处理逻辑与线程管理逻辑分离,从而让代码更便于理解和维护。
    • 任务提交简单:利用线程池技术,只需要将任务提交给线程池,线程池就会自己调度和执行任务,不需要程序员管理线程的声明周期。
  • 线程复用
    • 提高线程利用效率:线程池中的线程是可以重复进行使用,提高线程的使用效率以及系统的响应速度。
  • 负载均衡
    • 均衡的分配任务:线程池可以自动均匀的将任务分配给线程,从而实现负载均衡,避免个别线程过载
  • 减少上下文切换

缺点

  • 实现复杂,不方便进行调优
  • 资源竞争
    • 同步开销:多线程环境下,多个线程竞争访问共享资源从而带来同步开销,从而影响性能
    • 死锁风险:如果对同步和锁不当的管理,会造成死锁
  • 增加内存占用
    • 提前创建线程会增加内存开销:如果处理的任务量过多,线程的创建会占用更多的内存。
  • 响应时间不确定

 单例模式改进后代码

#pragma once #include<iostream>
#include<queue>
#include<pthread.h>
#include<functional>
#include<memory>
#include<mutex>
#include<unistd.h>
#include"Task.hpp"#define NUM 6class ThreadPoll
{private:int num;//线程池中线程个数bool stop;//错误处理标志std::queue<Task> task_queue;//任务队列pthread_mutex_t lock;//互斥量pthread_cond_t cond;//条件变量ThreadPoll(int _num = NUM):num(_num),stop(false){pthread_mutex_init(&lock,nullptr);pthread_cond_init(&cond,nullptr);}~ThreadPoll(){pthread_mutex_destroy(&lock);pthread_cond_destroy(&cond);} //改进1// ThreadPoll(const ThreadPoll&){};ThreadPoll(const ThreadPoll&) = delete;ThreadPoll&operator = (const ThreadPoll&) = delete;static std::unique_ptr<ThreadPoll>single_instance;static std::once_flag initInstanceFlag;public://线程池单例设计static ThreadPoll*getinstance(){// static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER;// if(single_instance == nullptr)// {//     pthread_mutex_lock(&_mutex);//     if(single_instance == nullptr)//     {//         //类内创建线程池并完成初始化//         single_instance = new ThreadPoll();//         single_instance->InitThreadPoll();//     }//     pthread_mutex_unlock(&_mutex);// }// return single_instance;//改进3,使用C++11对单例模式进行升级std::call_once(initInstanceFlag,[](){single_instance.reset(new ThreadPoll());single_instance->InitThreadPoll();});return single_instance.get();}bool InitThreadPoll(){for(int i =0;i<num;i++){pthread_t tid;//线程从线程池中拿取任务执行,需要通过this指针if(pthread_create(&tid,nullptr,ThreadRoutine,this)!=0){LOG(FATAL,"create thread pool error ");return false;}}LOG(INFO,"create thread pool success ");return true;}void PushTask(const Task&task){Lock();task_queue.push(task);//将任务放到任务队列中Unlock();ThreadWakeup();}//改进2void StopAll(){Lock();stop = true;//唤醒所有线程pthread_cond_broadcast(&cond);Unlock();}//判断线程是否退出bool IsStop(){return stop;}bool TaskQueueIsEmpty(){return task_queue.size()==0?true:false;}void Lock(){pthread_mutex_lock(&lock);}void Unlock(){pthread_mutex_unlock(&lock);}void ThreadWait(){//使用条件变量,条件变量满足时唤醒线程继续执行任务pthread_cond_wait(&cond,&lock);}void ThreadWakeup(){pthread_cond_signal(&cond);}static void *ThreadRoutine(void*args){ThreadPoll*tp = (ThreadPoll*)args;while(true){Task t;tp->Lock();//如果任务队列中没有任务,则让线程进行休眠;防止伪唤醒的出现,使用while循环while (tp->TaskQueueIsEmpty() && !tp->IsStop()){tp->ThreadWait();}if(tp->IsStop()){tp->Unlock();break;}tp->PopTask(t);tp->Unlock();t.ProcessOn();}return nullptr;}  void PopTask(Task&task){task = task_queue.front();task_queue.pop();}};std::unique_ptr<ThreadPoll> ThreadPoll::single_instance;
std::once_flag ThreadPoll::initInstanceFlag;

当前线程池的替换方案

  • 异步编程
    • C++的boost::asiolibuv
      • boost.Asio是Boost库的一部分,主要提供了异步IO的操作,同时支持TCP、UDP等协议
      • libuv 是一个多平台的支持异步 I/O 的 C 库,最初用于 Node.js。它支持事件循环和异步 I/O 操作,使得构建高性能的网络应用程序变得更加容易
      • 优点:高效处理IO操作,减少线程切换带来的开销
      • 缺点:编程模型复杂,需要管理异步操作的生命周期
    • 协程
      • 协程是轻量化的用户态线程,可以在单个线程中实现类似于多线程的并发操作
      • 优点:简化异步编程模型,代码可读性高,性能开销小
      • 缺点:需要现代编译器的支持
    • Reactor/Proactor模型
      • 事件驱动的并发模型,boost::asio就是基于Reactor模型实现的
      • 优点:高效处理并发IO请求
      • 缺点:需要理解和实现事件驱动机制,编程模型复杂
    • Actor模型
      • 并发编程模型,通过消息传递进行通信,常见的包括Erlang和Akka
      • 优点:支持并发和分布式,模型清晰
      • 缺点:代码改动大
    • IO密集型应用适合异步编程或者Reactor模型;CPU密集型任务则使用线程池;携程和Actor模型在高并发或者分布系统中表现出色。

总结:主要是修改思路的一些总结,后面代码的具体改进也是基于此处的一些探索,非最终版本。 

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

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

相关文章

Java 随笔记: 集合与泛型

文章目录 1. 集合框架概述2. 集合接口2.1 Collection 接口2.2 List 接口2.3 Set 接口2.4 Map 接口 3. 集合的常用操作3.1 添加元素3.2 删除元素3.3 遍历元素3.4 判断大小3.5 判断是否为空 4. 迭代器4.1 迭代器的作用4.2 迭代器的使用4.3 迭代器与增强 for 循环4.4 迭代器的注意…

异步IO的概念以及使用asyncio库进行异步编程的示例。

异步IO的概念 异步IO&#xff08;Asynchronous Input/Output&#xff09;是一种IO操作模式&#xff0c;它允许程序在等待IO操作&#xff08;如文件读写、网络请求等&#xff09;完成时继续执行其他任务&#xff0c;而不是阻塞等待IO操作完成。这种机制可以显著提高程序的性能和…

深入剖析Tomcat整体架构

目录 Tomcat简介Tomcat架构概述核心组件详解 ServerServiceConnectorEngineHostContextWrapper 生命周期与初始化请求处理流程Tomcat的线程模型配置与优化常见问题与解决方案总结 Tomcat简介 Apache Tomcat是由Apache软件基金会开发的开源Java Web服务器和Servlet容器。它实…

【LeetCode】对称二叉树

目录 一、题目二、解法完整代码 一、题目 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true 示例 2&#xff1a; 输入&#xff1a;root [1,2,2,null,3,null,3] 输出&#…

友力科技数据中心搬迁方案

将当前运行机房中的所有设备、应用系统安全搬迁至新数据中心机房&#xff0c;实现平滑切换、平稳过渡&#xff0c;最大限度地降低搬迁工作对业务的影响。 为了确保企事业单位能够顺利完成数据中心机房搬迁工作&#xff0c;我们根据实际经验提供了4个基本原则&#xff0c;希望能…

异步电机矢量控制matlab simulink

1、内容简介 略 86-可以交流、咨询、答疑 异步电机、矢量控制 2、内容说明 略 3、仿真分析 略 4、参考论文 略

YOLOv2小白精讲

YOLOv2是一个集成了分类和检测任务的神经网络&#xff0c;它将目标检测和分类任务统一在一个单一的网络中进行处理。 本文在yolov1的基础上&#xff0c;对yolov2的网络结构和改进部分进行讲解。yolov1的知识点可以看我另外一篇博客&#xff08;yolov1基础精讲-CSDN博客&#xf…

设计模式-抽象工厂

抽象工厂属于创建型模式。 抽象工厂和工厂设计模式的区别&#xff1a; 工厂模式的是设计模式中最简单的一种设计模式&#xff0c;主要设计思想是&#xff0c;分离对象的创建和使用&#xff0c;在Java中&#xff0c;如果需要使用一个对象时&#xff0c;需要new Class()&#xff…

day18-binary tree-part06-7.20

tasks for today: 1. 530.二叉搜索树的最小绝对差 2. 501.二叉搜索树中的众数 3. 236.二叉树的最近公共祖先 ----------------------------------------------------------------- 1. 530.二叉搜索树的最小绝对差 in this practice, the target tree is a binary search …

RAG-LLM Survey

大模型虽然厉害&#xff0c;但是存在着幻觉、知识陈旧等问题。检索增强生成&#xff08;Retrieval-Augmented Generation, RAG&#xff09;可以通过挂载外部知识库&#xff0c;来提升生成内容的准确性和可信度。了解一个研究方向的最快的方法&#xff0c;就是阅读相关的综述。今…

Python数据可视化------动态柱状图

一、基础柱状图 # 基础柱状图 # 导包 from pyecharts.charts import Bar from pyecharts.options import *# 构建柱状图 bar Bar() # 添加数据&#xff08;列表&#xff09; x_list ["张三", "李四", "王五", "赵六"] y_list [50,…

你的生产车间有个好“布局”吗?

对于生产车间而言&#xff0c;科学合理的布局设计便是这“成功的一半”。在现代制造环境中&#xff0c;高效的物流动线、合理的设备配置、以及人性化的工作环境&#xff0c;是提升生产效率、保障产品质量的关键。 一个好布局的生产车间需具备以下几个关键特征&#xff0c;以确保…

【ADRC笔记】LESO-Wb

公式推导(bilibili) 一阶ESO 二阶ESO 二阶自抗扰控制器基本原理 选取状态变量 观测器收敛性推导 wo 观测器带宽

【ubuntu 网卡混杂模式设置】

ubuntu 网卡混杂模式设置 在 ubuntu上设置混杂模式确保防火墙允许混杂模式检查网卡是否已设置为混杂模式使用 ifconfig使用 ethtool使用 ip link 在 ubuntu上设置混杂模式 本文介绍在Ubuntu系统上查看网卡是否是混杂模式&#xff0c;以及设置混杂模式。 确保防火墙允许混杂模…

数据结构(下)复习

一丶数据结构概述 1.数据结构是一门研究非数值计算的程序设计问题中计算机的数据元素以及它们之间的关系和运算等的学科。 2.基本数据结构&#xff0c;从逻辑上可分为线性结构和非线性结构&#xff0c;大体上可分成表结构、树结构、图结构 和散结构4大类 表结构用于表示结点的…

Java 滑动时间窗口统计接口调用次数

Java 滑动时间窗口统计接口调用次数 在分布式系统中&#xff0c;接口调用次数的监控是一个非常重要的任务。它可以帮助我们了解系统的负载情况&#xff0c;及时发现性能瓶颈&#xff0c;以及为系统扩容提供依据。在这篇文章中&#xff0c;我们将讨论如何使用 Java 实现滑动时间…

科普文:百度交易中台之订单系统架构浅析

百度交易中台作为集团移动生态战略的基础设施&#xff0c;面向收银交易与清分结算场景&#xff0c;为赋能业务提供高效交易生态搭建。目前支持百度体系内多个产品线&#xff0c;主要包含&#xff1a;小程序&#xff0c;地图打车&#xff0c;百家号&#xff0c;招财猫&#xff0…

算法题目整合

文章目录 121. 小红的区间翻转142. 两个字符串的最小 ASCII 删除总和143. 最长同值路径139.完美数140. 可爱串141. 好二叉树 121. 小红的区间翻转 小红拿到了两个长度为 n 的数组 a 和 b&#xff0c;她仅可以执行一次以下翻转操作&#xff1a;选择a数组中的一个区间[i, j]&…

自然语言处理基础【1】词嵌入

词嵌入&#xff08;Word Embedding&#xff09; 词嵌入&#xff08;Word Embedding&#xff09;是一种自然语言处理技术&#xff0c;用于将词语或短语转换为实数向量&#xff0c;以便机器能够理解和处理语言。它通过捕捉词语之间的语义和语法关系&#xff0c;能够在高维空间中…

企业微信PC版应用跳转到默认浏览器,避坑指南,欢迎补充(Vue项目版)。。。

引子 关于企业微信PC版应用跳转到默认浏览器&#xff0c;我之前写过一篇文章&#xff1a;企业微信PC版应用跳转到默认浏览器&#xff0c;避坑指南&#xff0c;欢迎补充。。。 以前的文章里用的前后端一体的Jsp项目&#xff0c;这次我使用的是前后端分离的Vue项目&#xff0c;…