开源:evpp-基于 libevent 开发高性能网络服务器
leonora806
8年前
<h2>evpp</h2> <h2>简介</h2> <p><a href="/misc/goto?guid=4959740702627795088" rel="nofollow,noindex">evpp</a> 是一个基于 <a href="/misc/goto?guid=4959629545116891208" rel="nofollow,noindex">libevent</a> 开发的现代化C++11高性能网络服务器,自带TCP/UDP/HTTP等协议的异步非阻塞式的服务器和客户端库。</p> <h2>特性</h2> <ol> <li>现代版的C++11接口</li> <li>非阻塞异步接口都是C++11的functional/bind形式的回调仿函数(不是 libevent 中的C风格的函数指针)</li> <li>非阻塞纯异步多线程TCP服务器/客户端</li> <li>非阻塞纯异步多线程HTTP服务器/客户端</li> <li>非阻塞纯异步多线程UDP服务器</li> <li>支持多进程模式</li> <li>优秀的跨平台特性和高性能(继承自 libevent 的优点)</li> </ol> <p>除此之外,基于该库之上,还提供两个附带的应用层协议库:</p> <ol> <li>evmc :一个纯异步非阻塞式的 memcached 的C++客户端库,支持 membase 集群模式。详情请见: <a href="/misc/goto?guid=4959740702749572291" rel="nofollow,noindex">e</a>vmc readme</li> <li>evnsq : 一个纯异步非阻塞式的 NSQ 的C++客户端库,支持消费者、生产者、服务发现等特性。详情请见: evnsq readm<a href="/misc/goto?guid=4959740702829748869" rel="nofollow,noindex">e</a></li> </ol> <p>将来还会推出 redis 的客户端库。</p> <h2>项目由来</h2> <p>我们开发小组负责的业务需要用到TCP协议来建设长连接网关服务和一些其他的一些基于TCP的短连接服务,在调研开源项目的过程中,没有发现一个合适的库来满足我们要求。结合我们自身的业务情况,理想中的C++网络库应具备一下几个特性:</p> <ol> <li>接口简单易用,最好是C++接口</li> <li>多线程,也能支持多进程</li> <li>最好是基于 libevent 实现(因为现有的历史遗留框架、基础库等是依赖 libevent ),这样能很方便嵌入 libevent 的事件循环,否则改动较大或者集成起来的程序可能会有很多跨线程的调用</li> </ol> <p>基于这些需要,可供选择的不多,所以我们只能自己开发一个。开发过程中,接口设计方面基本上大部分是参考 muduo 项目来设计和实现的,当然也做了一些取舍和增改;同时也大量借鉴了 Golang 的一些设计哲学和思想,举几个小例子来说明一下:</p> <ol> <li>Duration : 这是一个时间区间相关的类,自带时间单位信息,参考了 Golang 项目中的 Duration 实现。我们在其他项目中见到太多的时间是不带单位的,例如 timeout ,到底是秒、毫秒还是微秒?需要看文档说明或具体实现,好一点的设计会将单位带在变量名中,例如 timeout_ms ,但还是没有 Duration 这种独立的类好。目前C++11中也有类似的实现 std::chrono::duration ,但稍显复杂,没有咱们这个借鉴 Golang 实现的版本来的简单明了。</li> <li>Buffer : 这是一个缓冲区类,融合了 muduo 和 Golang 两个项目中相关类的设计和实现</li> <li>http::Server : 这是一个http服务器类,自带线程池,它的事件循环和工作线程调度,完全是线程安全的,业务层不用太多关心跨线程调用问题。同时,还将http服务器的核心功能单独抽取出来形成 http::Service 类,是一个可嵌入型的服务器类,可以嵌入到已有的 libevent 事件循环中。</li> <li>网络地址的表达就仅仅使用 "ip:port" 这种形式字符串表示,就是参考 Golang 的设计</li> <li>httpc::ConnPool 是一个http的客户端连接池库,设计上尽量考虑高性能和复用。以后基于此还可以增加负载均衡和故障转移等特性。</li> </ol> <p>另外,我们实现过程中极其重视线程安全问题,一个事件相关的资源必须在其所属的 EventLoop 中初始化和析构释放,这样我们能最大限度的减少出错的可能。为了达到这个目标我们重载 event_add 和 event_del 等函数,每一次调用 event_add ,就在对应的线程私有数据中记录该对象,在调用 event_del 时,检查之前该线程私有数据中是否拥有该对象,然后在整个程序退出前,再完整的检查所有线程的私有数据,看看是否仍然有对象没有析构释放,详细代码实现可以参考 <a href="/misc/goto?guid=4959740702913337045" rel="nofollow,noindex">https://github.com/Qihoo360/evpp/blob/master/evpp/inner_pre.cc#L46~L87</a> 。我们如此苛刻的追求线程安全,只是为了让一个程序能安静的平稳的退出或Reload,因为我们深刻的理解“编写永远运行的系统,和编写运行一段时间后平静关闭的系统是两码事”,后者要困难的多。</p> <h2>快速开始</h2> <p>请见 <a href="/misc/goto?guid=4959740702996600582" rel="nofollow,noindex">Quick Start</a></p> <h2>Examples</h2> <h2>An echo TCP server</h2> <pre> #include <evpp/exp.h> #include <evpp/tcp_server.h> #include <evpp/tcp_conn.h> #include <evpp/buffer.h> void OnMessage(const evpp::TCPConnPtr& conn, evpp::Buffer* msg, evpp::Timestamp ts) { std::string s = msg->NextAllString(); LOG_INFO << "Received a message [" << s << "]"; conn->Send(s); if (s == "quit" || s == "exit") { conn->Close(); } } int main(int argc, char* argv[]) { std::string addr = std::string("0.0.0.0:9999"); evpp::EventLoop loop; evpp::TCPServer server(&loop, addr, "TCPEcho", 0); server.SetMessageCallback(&OnMessage); server.Init(); server.Start(); loop.Run(); return 0; }</pre> <h3>An echo HTTP server</h3> <pre> #include <evpp/exp.h> #include <evpp/http/http_server.h> void RequestHandler(evpp::EventLoop* loop, const evpp::http::ContextPtr& ctx, const evpp::http::HTTPSendResponseCallback& cb) { cb(ctx->body.ToString()); } int main(int argc, char* argv[]) { std::vector<int> ports = {9009, 23456, 23457}; int thread_num = 2; evpp::http::Server server(thread_num); server.RegisterHandler("/echo", &RequestHandler); server.Init(ports); server.Start(); while (!server.IsStopped()) { usleep(1); } return 0; }</pre> <h3>An echo UDP server</h3> <pre> #include <evpp/exp.h> #include <evpp/udp/udp_server.h> #include <evpp/udp/udp_message.h> void DefaultHandler(evpp::EventLoop* loop, evpp::udp::MessagePtr& msg) { evpp::udp::SendMessage(msg); } int main(int argc, char* argv[]) { std::vector<int> ports = {1053, 5353}; evpp::udp::Server server; server.SetMessageHandler(&DefaultHandler); server.Init(ports); server.Start(); while (!server.IsStopped()) { usleep(1); } return 0; }</pre> <h2>用户列表</h2> <p><img src="https://simg.open-open.com/show/0c061d096d286118cad5ec594cde0f85.png"></p> <p> </p> <p>来自:https://github.com/Qihoo360/evpp/blob/master/readme_cn.md</p> <p> </p>