实现通过网络来传输数据、需要网络通信类库, 大部分网络通信基础类库都是同步的. 一个TCP连接建立后、用户代码会得到用于收发数据的通道、每个通道会在内存开辟两片区域用于收发数据的缓存
发数据比较简单:
用户代码在发送时写入的数据会暂存在缓存中、然后操作系统会通过网卡、把发送缓存中的数据传输到对端的服务器上
只要缓存不满、或者发送数据的速度未超过网卡传输速度的上限、发送数据的操作耗时就只是写入一次内存的时间、同步发送即可、无需异步
接收数据比较麻烦、它不知道什么时候会收到数据同步IO
: 用一个线程阻塞、等待数据、数据到来、操作系统会先把数据写入到接收缓存、然后给接收数据的线程发通知、接收线程收到通知结束等待、开始读取数据、处理完将继续阻塞、等待下次数据到来
同步IO处理少量连接没问题、但同时处理大量连接的时候、每个连接都会阻塞一个线程来等待数据、这些连接都在收发数据的时候就会有大量的线程来抢占CPU的世界、造成频繁的CPU上下文切换、导致CPU的负载升高、系统性能下降
期望值:
事先定义好收到数据后的处理逻辑、将它作为一个回调方法、收到数据后、网络通信框架直接回调这个方法就好
Java网络模型
极客时间留言区看到一个很不错的评论、收藏下
举例: 有一个养鸡的农场、里边养着各个农户Thread的鸡Socket、每个农户都在农场中建立了自己的鸡舍SocketCahnnel
BIO
: Bolck IO, 每个农户盯着自己的鸡舍、有下蛋、就去捡NIO
: No-Block IO, 单 Selector、农户们花钱请了一个饲养员Selector
, 并且告诉饲养员Register 若哪家的鸡下蛋要向农户报告 select keysNIO
: No-Block IO - 多Selector、鸡舍增多时、一个饲养员巡视(轮询)一次的时间增大、延迟较大、多请几个、每个饲养员分配几个鸡舍管理、减小延迟epoll
: 饲养员不巡查鸡舍、听到有鸡打鸣(活跃连接)就知道下单了AIO
: Asynchronous IO、鸡下单后、饲养员负责取蛋、通知农户来取即可、不需要农户自己到鸡舍取蛋
序列化和反序列化
TCP连接上、传输数据的基本形式是二进制流、0和1、在一般编程语言或者框架提供的api中、传输数据的基本形式是字节Byte、本质上是二进制流
编写的程序是结构化的数据, eg. 类或者结构体
显然, 要使用网络框架的API来传输结构化的数据、必须先实现结构化的数据和字节流之间的双向转换
文件内保存数据的形式也是二进制序列、所以、也需要序列化结构化数据才能实现
如何选择序列化方式
需要权衡的因素:
- 序列化后的数据易于阅读
- 实现复杂度低
- 序列化和发序列化越快越好
- 序列化后的信息密度越大越好、即同一个结构化刷数据、序列化后占用的空间越小越好
而 1和4 是矛盾的、2 和 3是矛盾的、所以需要根据业务场景合理选择
思考
在内存中存放的数据、最基础的存储单元也是二进制比特、也就是说应用程序操作的对象、在内存中也是使用二进制存储的、既然都是二进制、为什么不直接把二进制数据通过网络发送出去 ?
内存里的内容、不通用、不同系统不同语言的组织可能都是不一样的、而且存在很多引用、指针、并不是直接数据块
序列化、反序列化其实是约定一种标准、大家都遵守就能实现跨语言、跨平台
内存管理
高并发下的内存管理技巧
优化代码中处理请求的业务逻辑、减少创建一次性对象、eg. 可以将受到请求的Request对象在业务流程中尽量传递下去、而不是执行一个步骤就创建一个内容和Request对象差不多的新对象
使用频繁的对象、可以考虑建立一个对象池、收到请求后在对象池内申请一个对象、使用完放回对象池
可能的话、直接使用更大内存的服务器、也可以非常有效的缓解这个问题