• 1. 高性能并发网络服务器 设计与实现 http://www.ideawu.net/
  • 2. 网络服务器开发协议设计(网络协议和应用协议) 语法(报文格式) 语义(指令的处理, 交互时序等) 只要涉及到交互(即使不是网络交互), 就需要协议 网络实现(IO) 网络开发的基础是socket 任何对网络的封装, 在三度关系之内必然是socket(最多三层封装)
  • 3. 第一节 – 网络IO
  • 4. Talk is cheap, show me the codeserv = new TcpSocket(); serv.listen(); sock = serv.accept(); sock.read(buf); sock.write(buf); sock.close() 一次服务 没有并发 短连接 有没有协议? 最简单的TCP服务器
  • 5. 重复服务serv = new TcpSocket(); serv.listen(); while(1){ sock = serv.accept(); while(1){//重复服务单用户 sock.read(buf); sock.write(buf); } sock.close() } 一次服务 没有并发 短连接
  • 6. socket IOread/write是可阻塞的 阻塞是并发和高性能的死敌 实现并发和高性能的途径就是解阻塞 太简单了! 后面继续讲解read/write...
  • 7. 并发服务serv = new TcpSocket(); serv.listen(); while(1){ sock = serv.accept(); new Thread(){ while(1){//重复服务单用户 sock.read(buf); sock.write(buf); } sock.close() } } 一次服务 没有并发 短连接
  • 8. 优缺点优点: 简单易用, 一般工作得很好 多线程, 可以利用CPU多核 缺点: 启动线程也有成本, 很可能占大头 线程的数量不受控制, 危险! 问题在于IO...
  • 9. IO多路复用专业地解决一个问题 select/poll/epoll/kqueue的API几乎一样, 实现不同, 性能不同 IO多路复用接口的作用, 就是测试accept/read/write等IO调用会不会阻塞
  • 10. IO多路复用示例serv = new TcpSocket(); serv.listen(); select.add(serv, READ); // READ 表示只测试可读(读不阻塞) while(1){ readable, writable = select.wait(); foreach(readable as sock){ if(sock == serv){ sock = serv.accept(); select.add(sock, READ); }else{ sock.read(buf); sock.write(buf); sock.close(); } } // writable 为空, 因为我们不测试可写 }
  • 11. 还不完善write也可能阻塞, 修改... sock.read(buf); sock.write(buf); sock.close(); select.add(sock, WRITE); foreach(writable as sock){ sock.write(buf); sock.close(); }
  • 12. 第二节 – 报文解析
  • 13. 报文设计最重要的TCP协议是流式协议, 但几乎所有的应用协议都是基于报文的协议 报文分隔 用连接关闭来表示报文结束. 如, HTTP/1.0的响应 固定长度的报文. 如, TFTP的数据报文. 带自描述长度的固定长度首部的变长报文. 如IP包, TCP分段. 带结束符. 如, 行协议, HTTP协议. 逐字节解析和数据转义的影响. 高层文本, 带结束符底层二进制, 固定长度
  • 14. SSDB的报文格式Packet := Block+ '\n' Block := Size Data Size := literal_integer '\n' Data := size_bytes_of_data '\n' 示例: 3 get 3 key <回车>
  • 15. 优点简单 带长度字段, 支持二进制数据 同时对人和对机器友好, 报文数据文本化效果好. 可以telnet到服务器进行交互 解析器非常简单, PHP代码不过几十行 同时兼容LF和CRLF(惯例)
  • 16. socket io 2read/write读取/发送的是字节数组. C语言的char[], PHP的string read返回时读取的准确字节数无法预计 导致"粘包", "断包" write返回时不表示数据已到达对方机器 所以, 即便基于可靠传输的TCP协议, 也需要应用层协议进行确认来保证真正意义上的"可靠"
  • 17. 带有报文解析的服务器serv = new TcpSocket(); serv.listen(); sock = serv.accept(); client = new Client(sock); client.recv(packet); client.send(packet); client.close()packet一般是编程语言中的对象
  • 18. 序列化和反序列化read收到的是字节数组, 要进行反序列化转成编程语言的对象 反序列化的过程就是报文解析
  • 19. 解析报文bytes = []; while(1){ bytes += sock.read(); if(try_parse(bytes, &len) == READY){ // 已经解析出了一个报文, 报文的长度是len // 从字节数组中清除掉已解析的 bytes.remove(len); } }
  • 20. 整合网络IO和报文解析将上例中的read和try_parse分离 作业...
  • 21. 第三节 – 业务处理网络服务器 = 协议处理 + 网络IO + 业务处理
  • 22. 业务处理的位置serv = new TcpSocket(); serv.listen(); sock = serv.accept(); client = new Client(sock); request = client.recv(); response = process(request); client.send(response); client.close()对于SSDB, LevelDB 相关的操作封装在 process 中.
  • 23. 剩下的...就是看代码写代码...
  • 24. FINThanks SSDB: https://github.com/ideawu/ssdb