Skip to content

liukang1995/A-high-proformance-server-on-linux

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

69 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

A tiny  server 

简介:
    为了能够方便的获取交换机自身的信息,需要交换机主动向客户端暴露相应的接口。为此,我们需要在交换机上实现一个服务程序,它通过解析交换机相应的日志文件和相关信息,向请求端回送相关数据。
    它是一个基于C++多线程、Reactor反应式HTTP请求解析服务器服务器。它为客户端提供解析交换机数据的接口。它参考了部分MUDUO、Redis数据库服务器的设计。目前,能够做到解析GET、HEAD两种HTTP请求。并可以解析一定的请求头部,如Host、Content-type、Keep-Alive等头部。
    它主要具有以下特征:
    基于EPOLL的IO多路复用机制。服务器只封装了POSIX epoll机制。因为他在高并发的情况下保持着相对稳定的吞吐量。
    基于对象而非基于面向对象的程序设计。每一个类都代表一份实体。它通常不可复制。传统的设计模式,如策略模式,采用std::function代替。
    基于线程的程序设计(而非基于任务)。在C++11之后,基于任务的程序设计成为一种推荐,但本服务器依然采用基于线程的设计。在代码中,实现了一个简易的线程池,它用于启动多个线程,并轮询接受一个为一个fd读写。在
    专用的服务器部署基于线程的服务器设计,线程池依然成为首选。
    更多的C++11、C++14机制。如:lambda代替了所有回调传统场合,C++14广义lambda的出现,完全代替了已经从boost,tr1库融入标准库的std::bind函数。lambda拥有比std::bind更优的代码。
                            using别名声明代替了typedef。enum class代替了enum,这是C++提供的强类型枚举量。
                            针对一次性通信事件,采用基于std::promise和std::future的实现。主要应用在启动线程的场合。它代替了传统的condition_variable的使用场景,是线程不在阻塞在条件变量上,而是阻塞在
                            期值的获取上,它没有虚假唤醒等问题,却需要一份堆分配空间的开销。但仍然不失为一个好方法。
                            智能指针的加入。除个别对象指针外(如eventloop),其他对象均以智能对象管理。
                            以auto声明部分变量进行初始化。



计时器设计:
    名称:timernode,timer
    在REDIS服务器中,以无序链表管理所有的时间时间。时间事件执行与文件事件之后。每次执行事件都需要遍历整个链表以找到所有到期的事件。在Ridis服务器
    在MUDUO网络库中,以std::set管理整个时间事件。set采用红黑树设计,每次查找需O(lgn)时间。
    在本服务器中,时间事件以优先级队列实现。它在C++11中,已经实现为std::priority_queue。它的底层采用堆设计,它却依赖于支持随机迭代器的容器并支持指定的成员函数。此处采用deque为底层容器。
    删除策略:惰性删除策略,优先级队列不支持随机访问,因此,一旦插入优先级队列,除非超时否则不会提前清除。取消计时器通过设计deleted标志位实现。这可能会造成一定的内存泄漏。
    生命周期:有timeManage.add创建。直到超时。而后者由EPOLL CTL系列函数调用。计时器主要用来从epoll分离channel。为保证能够快速访问计时器节点。在每一个httpconnection里持有一个timenode的weak_ptr。以空间换时间。

客户端设计:
    名称:client
    客户端种类:仅有一种客户端,即普通的发送HTTP请求的客户端。
    生命周期:由server创建,生命期交由server管理。代表了一份HTTP客户端。同时,channel,timenode持有一份httpconnection的弱引用。当计时器超时,channel分离之后,应该启动httpconnection的析构行为。
    状态属性:主要包括TCP连接状态 ConnectionState,事务处理状态ProcessState,headerstate,uristate。处理进度:analysisstate
    请求方法:method_ 解析头:headers_ 描述符fd_
    提供的回调函数:handleRead有数据到来的读回调。handlewrite写数据回调。handleerror用来处理错误,主要供handleread使用,发送错误回客户端
    缓冲区:直接采用了string作为缓冲区。可能会带来一定的内存风险,和一定的效能损失。
    Redis客户端的设计:属性:描述符,名字,标志(包括角色和状态),命令与命令参数。命令的实现函数。身份认证,时间等。这些在httpconnection部分实现。

文件事件分派器:
    名称:channel
    功能:负责分派一个服务器读写事件、或客户端读写事件、或eventfd读写事件
    声明周期:随其创建者生而生,随其创建者死而死。如,当服务器接收一个连接时,创建一个client伴随着一个channel
    持有者:创建者、IO多路复用程序。当创建者死亡时,应提前将其从多路复用程序中分离。
    回调事件由其创建者设定,关心的事件由创建者初始化,并可能在处理函数中被修改。

IO多路复用程序:
    名称:epoll
    功能:掌管fd的监听,并负责管理fd对应的channel的增删改、核心为epoll_wait,返回活动的channels,供文件事件处理器调用。
    生命周期:由文件事件处理器eventloop掌管,channel持有它的弱引用。随eventloop生而生,随eventloop死而死。
 

文件事件处理器:
    名称:eventloop
    功能:负责整个事件的调度、即整个事件循环:包括:IO多路复用程序、为活动事件分派、调用相应的事件处理程序。
    声明周期:为了在线程没有启动前并不实际分配空间。所以创建为线程的栈上对象。保证它的声明周期比以上程序要长,所以采用普通的指针进行管理。它通常由channel,client,server持有。当离开线程栈是,eventloop应析构。

服务器设计:
    名称:server
    功能:监听文件描述符,当有新的客户端连接时,利用连接应答处理器处理读事件。并为新的客户连接创建新的客户端,并将客户端绑定至其他线程。一个描述符自始至终都由一个线程进行读写。保证线程安全性。
    生命周期:在主线程创建的堆上对象,并且注册在主线程的事件循环上,因此,声明周期为整个进程的周期。
    持有者:每一个客户端持有server的指针。保证server的生命周期长于任何一个client;

线程池设计:
    名称:threadpool
    功能:创建指定大小的线程,每一个线程将运行事件循环。只有当调用start()成员函数后,才真正开始创建线程。
    生命周期:主线程在初始化数据结构时创建。它的一个指针由server进行管理。成员函数也由server进行调用。声明周期和进程一样长。

信号处理:
    忽略SIGPIPE。

About

multi-thread

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages