从零开始实现一个C++高性能服务器框架----Servlet模块

简介

项目介绍:实现了一个基于协程的服务器框架,支持多线程、多协程协同调度;支持以异步处理的方式提高服务器性能;封装了网络相关的模块,包括socket、http、servlet等,支持快速搭建HTTP服务器或WebSokcet服务器。

详细内容:日志模块,使用宏实现流式输出,支持同步日志与异步日志、自定义日志格式、日志级别、多日志分离等功能。线程模块,封装pthread相关方法,封装常用的锁包括(信号量,读写锁,自旋锁等)。IO协程调度模块,基于ucontext_t实现非对称协程模型,以线程池的方式实现多线程,多协程协同调度,同时依赖epoll实现了事件监听机制。定时器模块,使用最小堆管理定时器,配合IO协程调度模块可以完成基于协程的定时任务调度。hook模块,将同步的系统调用封装成异步操作(accept, recv, send等),配合IO协程调度能够极大的提升服务器性能。Http模块,封装了sokcet常用方法,支持http协议解析,客户端实现连接池发送请求,服务器端实现servlet模式处理客户端请求,支持单Reator多线程,多Reator多线程模式的服务器。

Servlet模块

HTTP Servlet包括两部分,第一部分是Servlet对象,每个Servlet对象表示一种处理HTTP消息的方法,第二部分是ServletDispatch,它包含一个请求路径到Servlet对象的映射,用于指定一个请求路径该用哪个Servlet来处理。

1. 主要功能

  • 提供HTTP请求路径到处理类的映射,用于规范化的HTTP消息处理流程。

2. 功能演示

  • 一个简单的服务器,由ServletDispatch完成uri对应的逻辑处理
johnsonli::http::HttpServer::ptr server(new johnsonli::http::HttpServer(true));
johnsonli::Address::ptr addr = johnsonli::Address::LookupAnyIPAddress("0.0.0.0:8020");
while(!server->bind(addr)) {
    sleep(2);
}

// "/sylar/xx"映射
auto dsp = server->getServletDispatch();
dsp->addServlet("/sylar/xx", [](johnsonli::http::HttpRequest::ptr req
                                , johnsonli::http::HttpResponse::ptr rsp
                                , johnsonli::http::HttpSession::ptr session){
    rsp->setBody(req->toString());
    return 0;                           
});

// "/sylar/*"映射
dsp->addGlobServlet("/sylar/*", [](johnsonli::http::HttpRequest::ptr req
                                , johnsonli::http::HttpResponse::ptr rsp
                                , johnsonli::http::HttpSession::ptr session){
    rsp->setBody("Glob:\r\n" + req->toString());
    return 0;                           
});

server->start();

3. 模块介绍

3.1 Servlet

  • 基类,用于封装处理请求的方法
class Servlet {
public:
    typedef std::shared_ptr<Servlet> ptr;
    Servlet(const std::string& name)
        :m_name(name) {}

    virtual ~Servlet() {}

    /**
     * @brief 处理请求
     * @param[in] request HTTP请求
     * @param[in] response HTTP响应
     * @param[in] session HTTP连接
     * @return 是否处理成功
     */
    virtual int32_t handle(johnsonli::http::HttpRequest::ptr request
                , johnsonli::http::HttpResponse::ptr response
                , johnsonli::http::HttpSession::ptr session) = 0;

    const std::string& getName() const { return m_name;}

protected:
    std::string m_name;     // servlet名称
};

3.2 FunctionServlet

  • 继承Servlet,封装了一个回调函数m_cb,用于具体的逻辑处理
class FunctionServlet : public Servlet {
public:
      /// 智能指针类型定义
      typedef std::shared_ptr<FunctionServlet> ptr;
      
      /// 函数回调类型定义
      typedef std::function<int32_t (johnsonli::http::HttpRequest::ptr request
                  , johnsonli::http::HttpResponse::ptr response
                  , johnsonli::http::HttpSession::ptr session)> callback;


      /**
       * @brief 构造函数
       * @param[in] cb 回调函数
       */
      FunctionServlet(callback cb);
      virtual int32_t handle(johnsonli::http::HttpRequest::ptr request
                  , johnsonli::http::HttpResponse::ptr response
                  , johnsonli::http::HttpSession::ptr session) override;
private:
      /// 回调函数
      callback m_cb;
};
  • handle方法处理逻辑实际上是调用的m_cb回调函数
int32_t FunctionServlet::handle(johnsonli::http::HttpRequest::ptr request
           , johnsonli::http::HttpResponse::ptr response
           , johnsonli::http::HttpSession::ptr session) {
    //调用回调
    return m_cb(request, response, session);
}

3.3 NotFoundServlet

  • 对于没有uri映射的Servlet,统一使用这个进行默认处理,返回一个404页面
/**
* @brief NotFoundServlet(默认返回404)
*/
class NotFoundServlet : public Servlet 
{
public:
   typedef std::shared_ptr<NotFoundServlet> ptr;

   NotFoundServlet(const std::string& name);

   virtual int32_t handle(johnsonli::http::HttpRequest::ptr request
               , johnsonli::http::HttpResponse::ptr response
               , johnsonli::http::HttpSession::ptr session) override;

private:
   std::string m_name;
   std::string m_content;
};

int32_t NotFoundServlet::handle(johnsonli::http::HttpRequest::ptr request
               , johnsonli::http::HttpResponse::ptr response
               , johnsonli::http::HttpSession::ptr session) {
	   response->setStatus(johnsonli::http::HttpStatus::NOT_FOUND);
	   response->setHeader("Server", "sylar/1.0.0");
	   response->setHeader("Content-Type", "text/html");
	   response->setBody(m_content);
	   return 0;
}

3.4 ServletDispatch

  • 继承Servlet,用于管理所有uri到Servle的映射unordered_map<std::string, Servlet::ptr>
class ServletDispatch : public Servlet 
{
public:
     typedef std::shared_ptr<ServletDispatch> ptr;
     typedef RWMutex RWMutexType;

     ServletDispatch();

     virtual int32_t handle(johnsonli::http::HttpRequest::ptr request
                , johnsonli::http::HttpResponse::ptr response
                , johnsonli::http::HttpSession::ptr session) override;
                
    // addServlet, delServlet, addGlobServlet, delGlobServlet, ...
private:
     /// 读写互斥量
     RWMutexType m_mutex;
     /// 精准匹配servlet MAP
     /// uri(/sylar/xxx) -> servlet
     std::unordered_map<std::string, Servlet::ptr> m_datas;
     /// 模糊匹配servlet 数组
     /// uri(/sylar/*) -> servlet
     std::vector<std::pair<std::string, Servlet::ptr>> m_globs;
     /// 默认servlet,所有路径都没匹配到时使用
     Servlet::ptr m_default;       
}; 
  • handle方法,通过request的uri,找到对应的Servlet,然后执行其对应的业务逻辑处理方法handle
int32_t ServletDispatch::handle(johnsonli::http::HttpRequest::ptr request
           , johnsonli::http::HttpResponse::ptr response
           , johnsonli::http::HttpSession::ptr session)  {
    auto slt = getMatchedServlet(request->getPath());
    if(slt) {
        slt->handle(request, response, session);
    }
    return 0;
} 
  • 如何完成映射?这里有三种映射的方式,一个是精确匹配,一个是模糊匹配,如果都没有匹配到,还有一个默认的servlet处理(NotFoundServlet)。
/// 精准匹配servlet MAP
/// uri(/sylar/xxx) -> servlet
std::unordered_map<std::string, Servlet::ptr> m_datas;
/// 模糊匹配servlet 数组
/// uri(/sylar/*) -> servlet
std::vector<std::pair<std::string, Servlet::ptr>> m_globs;
/// 默认servlet,所有路径都没匹配到时使用
Servlet::ptr m_default;
  • 精确匹配优先级大于模糊匹配
 Servlet::ptr ServletDispatch::getMatchedServlet(const std::string& uri) {
    RWMutexType::ReadLock lock(m_mutex);

    //先找m_datas,优先级更高
    auto mit = m_datas.find(uri);
    if(mit != m_datas.end()) {
        return mit->second;
    }

    //再找m_globs
    for(auto it = m_globs.begin();
            it != m_globs.end(); ++it)  {
        if(!fnmatch(it->first.c_str(), uri.c_str(), 0))  {
            return it->second;
        }
    }
    return m_default;
}

3.5 HttpServer中使用Servlet

  • 当服务器收到一个请求时,将请求给ServletDispatch,由ServletDispatch负责uri的不同处理。
class HttpServer : public TcpServer {
public:
	// ...
    ServletDispatch::ptr getServletDispatch() const { return m_dispatch;}
    void setServletDispatch(ServletDispatch::ptr v) { m_dispatch = v;}

private:
    // ...
    ServletDispatch::ptr m_dispatch;    // Servlet分发器
};
  • handleClient中的一些改变,由ServletDispatch负责uri对应的业务逻辑处理
void HttpServer::handleClient(Socket::ptr client) {
    HttpSession::ptr session(new HttpSession(client));
    do{
        auto req = session->recvRequest();
        if(!req) 
        {
            LOG_DEBUG(g_logger) << "recv http request fail, errno="
                << errno << " errstr=" << strerror(errno)
                << " cliet:" << *client << " keep_alive=" << m_isKeepalive;
            break;
        }
        HttpResponse::ptr rsp(new HttpResponse(req->getVersion()
                            ,req->isClose() || !m_isKeepalive));

        //servlet dispatch负责处理对应的请求
        m_dispatch->handle(req, rsp, session);

        session->sendResponse(rsp); 

    }while(m_isKeepalive);
}
  • 19
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

熬夜写代码的平头哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值