SSDB 源码分析 – 网络框架概述
最近, 我对 SSDB 的代码进行了重构, 分离出了 libutil, libnet, libssdb 几个高度可复用的模块, 代码的模块化更清晰, 也更容易进行 SSDB 的源码分析.
这一次要分析的是网络模块. 网络模块包括一个服务器的代码框架和运行框架, 以及网络协议设计和解析.
协议
SSDB 的网络协议非常简单, 而且是业务无关的, 所以你可以把 SSDB 的网络协议应用于几乎所有类型的应用! 只要遵循 SSDB 的网络协议, 你就可以使用 ssdb-cli 命令行工具来连接, 与服务器交互, 或者方便调试. 你甚至可以使用 nc 命令来和任何一个支持 SSDB 网络协议的服务器交互.
虽然 SSDB 的网络协议很简单, 但是解析 SSDB 协议报文, 很多人的代码也往往写得不严谨, 不是没考虑到这种情况, 就是没考虑到那种情况. 最常见的错误是用 fgets() 之类的行级 IO 函数来读取.
关于 SSDB 协议报文的解析, 在协议文档中的例子很清楚, 就不再详解, 感兴趣的同学, 可以从几个方面想: 我为什么会这样写? 你会这样写吗? 你为什么会那样写?
网络服务器运行框架
SSDB 的网络服务器是一个多线程的服务器框架, 进行了良好封装, 使用起来很方便. 可以看看这个例子.
#include "server.h" #include "../util/config.h" DEF_PROC(hello); int main(int argc, char **argv){ Config conf; conf.set("server.port", "9000"); NetworkServer serv; serv.init(conf); // register command procedure serv.proc_map.set_proc("hello", proc_hello); serv.serve(); return 0; } int proc_hello(NetworkServer *net, Link *link, const Request &req, Response *resp){ resp->push_back("ok"); resp->push_back("world!"); if(req.size() > 1){ // The first argument starts at index 1, just like argv. resp->push_back(req[1].String()); } return 0; }
在 main 函数中, 启动了服务器, 并在
serv.serve();
阻塞一直处理, NetworkServer 内部对信号(signal)进行了处理, 所以你不能再对 signal 进行处理了.
serv.proc_map.set_proc("hello", proc_hello);
这行代码注册了命令的处理函数, 命令的名字是 "hello", 对应的处理函数是 proc_hello.
处理函数中, req 参数是数组形式的请求参数列表, 你可以理解为 main 函数的 argv 参数, req[0] 是命令, 之后的是参数列表.
参数 resp 也是以类似数组的形式返回响应, 你只要把想返回的数据添加到 resp 中, 网络框架会帮你做完所有的事.
启动起来服务器进程之后, 你就可以用 ssdb-cli 工具连接上去, 操作过程如下:
$ ./tools/ssdb-cli 9000 ssdb (cli) - ssdb command line tool. Copyright (c) 2012-2014 ssdb.io 'h' or 'help' for help, 'q' to quit. server version: 1.0 ssdb 127.0.0.1:9000> hello abc ok ['world!', 'abc'] (0.000 sec) ssdb 127.0.0.1:9000>
SSDB 的服务器进程 ssdb-server, 主体功能就是定义了很多个处理函数.
网络请求处理流程
SSDB 的网络框架的核心函数之一是:
void NetworkServer::serve();
这个函数是 IO 主线程, 主要进行网络连接的管理, 如接受客户端的新连接, 对连接进行读或者写网络操作. 如果连接有数据要读, 读完后判断协议报文是否 ready 决定加入 ready 队列. 对于 ready 队列中的连接, 那么接下来就该被这个函数处理了:
void NetworkServer::proc(ProcJob *job);
这个函数里首先根据请求(ProcJob)查找相应的命令结构体(Command), 找到之后, 根据命令的属性决定是在当前线程(主线程)中处理, 还是放到一个任务队列中, 由工作者线程池处理.
这两个函数属 serve() 最为复杂, 因为它要判断连接的各种状态, 小心地进行处理. 所以函数的代码函数很多, 还包括几个子函数, 逻辑也不容易理解.
SSDB 使用网络框架使用 IO 多路复用机制来作为整个程序的主循环和主体结构, 而且由于 Linux 等 Posix 系统对于文件描述符都是可以进行 select 的, 所以, 工作者线程池和主线程之间的通信也可以被当成网络 IO 一样的逻辑, 这其中涉及到了一个名字叫 SelectableQueue 的数据结构. 主线程收到的请求通过 SelectableQueue 传递给工作者线程, 而工作者线程处理完请求后, 将响应再通过 SelectableQueue 传递回主线程.
建议继续学习:
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:ideawu 来源: idea's blog
- 标签: SSDB 网络框架
- 发布时间:2015-01-14 13:31:36
- [69] IOS安全–浅谈关于IOS加固的几种方法
- [66] Twitter/微博客的学习摘要
- [64] 如何拿下简短的域名
- [62] android 开发入门
- [60] find命令的一点注意事项
- [59] Go Reflect 性能
- [57] 流程管理与用户研究
- [56] Oracle MTS模式下 进程地址与会话信
- [56] 图书馆的世界纪录
- [55] 读书笔记-壹百度:百度十年千倍的29条法则