简介
项目介绍:实现了一个基于协程的服务器框架,支持多线程、多协程协同调度;支持以异步处理的方式提高服务器性能;封装了网络相关的模块,包括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);
}