IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

SSDB 源码分析 – 网络框架概述

idea's blog 2015-01-14 13:31:36 累计浏览 2,735 次
本机暂存

   最近, 我对 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 传递回主线程.

同分类推荐文章

  1. 使用deepseek进行Oracle恢复,引起重大故障 (2026-06-22 10:56:00)
  2. 接手一个只差临门一脚的数据库恢复 (2026-06-18 00:13:09)
  3. 我做了一个 AI 版的 StarRocks 升级风险扫描工具,直接帮我定位到一个风险 (2026-06-15 01:00:00)

查看更多 数据库 文章 →

建议继续学习

  1. 找回linux丢失的磁盘空间 (累计阅读 8,540)
  2. 如何查看Linux 硬件配置信息 (累计阅读 7,265)
  3. Vim 中截取部分内容保存到其他文件 (累计阅读 7,192)
  4. 神探tcpdump第二招 (累计阅读 5,873)
  5. VIM查找替换归纳总结 (累计阅读 5,389)
  6. 网络流量监控软件vnStat (累计阅读 4,957)
  7. VIM常用指令 (累计阅读 3,897)
  8. 软件推荐:curl (累计阅读 3,715)
  9. mac下的tree命令 (累计阅读 3,469)
  10. top监控命令在FreeBSD上的使用 (累计阅读 3,431)