如何编写一个 SendFile 服务器

如何编写一个 SendFile 服务器

前言

之前讨论零拷贝的时候,我们知道,两台机器之间传输文件,最快的方式就是 send file,众所周知,在 Java 中,该技术对应的则是 FileChannel 类的 transferTo 和 transferFrom 方法。

在平时使用服务器的时候,比如 nginx ,tomcat ,都有 send file 的选项,利用此技术,可大大提高文件传输效能。

另外,可能也有人谈论 send file 的缺点,例如不能利用 gzip 压缩,不能加密。这里本文不做探讨。

纸上得来终觉浅,绝知此事要躬行。

那么,如何使用这两个 api 实现一个 send file 服务器和客户端呢?

想象一下,你写的 send file 服务器利用 send file 技术,利用万兆网卡,从各个 client 端 copy 海量文件,瞬间打爆你那 1TB 的磁盘和 48核的 CPU。并且,注意:只需很小的 JVM 内存就可以实现这样一台强悍的服务器。为什么?如果你知道 send file 的原理,就会知道,使用 send file 技术时, 在用户态中,是不需要多少内存的,数据都在内核态。

是不是很有成就感?什么?没有?那打扰了 🤣。

另外,关于 send file,我们都知道,由于是直接从内核缓冲区进入到网卡驱动,我们几乎可以称之为 “零拷贝”,他的性能十分强劲。

但是。

除了这个,还有其他的吗?答案是有的,send file 利用 DMA 的方式 copy 数据,而不是利用 CPU。注意,不利用 CPU 意味着什么?意味着数据不会进入“缓存行”,进一步,不会进入缓存行,代表着缓存行不会因为这个被污染,再进一步,就是不需要维护缓存一致性。

还记得我们因为这个特性搞的那些关于 “伪共享” 的各种黑科技吗?是不是又学到了一点呢?😎

理念

作为一个纯粹的,高尚的,有趣的 sendFile 服务器或者客户端,使用场景是嵌入到某个服务中,或者某个中间件中,不需要搞成夸张的容器。我们可以借鉴一下,客户端可以做成 Jedis 那样的,如果你想搞个连接池也不是不可以,但 client 自身实例,还是单连接的。服务端可以做成 sun 的 httpServer 那种轻量的,随时启动,随时关闭。

同时, 支持 oneway 的高性能发送,因为,只要机器不宕机,发送到网卡就意味着发送成功,这样能大幅提高发送速度,减少客户端阻塞时间。

另外,也支持带有 ack 的稳定发送,即只有返回 ack 了,才能确认数据已经写到目标服务器磁盘了。

server 端支持海量连接,必须得是 reactor 网络模型,但我们不想在这么小的组件里用 netty,太重了,还容易和使用方有 jar 冲突。所以,我们可以利用 Java 的 selector + nio 自己实现 Reactor 模型。

设计

IO 模型设计

设计图:

image

如上图,Server 端支持海量客户端连接。

server 端含有 多个处理器,其中包括 accept 处理器,read 处理器 group, write 处理器 group。

accept 处理器将 serverSocketChannel 作为 key 注册到一个单独的 selector 上。专门用于监听 accept 事件。类似 netty 的 boss 线程。

当 accept 处理器成功连接了一个 socket 时,会随机将其交给一个 readProcessor(netty worker 线程?) 处理器,readProcessor 又会将其注册到 readSelector 上,当发生 read 事件时,readProcessor 将接受数据。

可以看到,readProcessor 可以认为是一个多路复用的线程,利用 selector 的能力,他高效的管理着多个 socket。

readProcessor 在读到数据后,会将其写入到磁盘中(DMA 的方式,性能炸裂)。

然后,如果 client 在 RPC 协议中声明“需要回复(id 不为 -1)” 时,那就将结果发送到 Reply Queue 中,反之不必。

当结果发送到 Reply Queue 后,writer 组中的 写线程,则会从 Queue 中拉取回复包,然后将结果按照 RPC 协议,写回到 client socket 中。

client socket 也会监听着 read 事件,注意:client 是不需要 select 的,因为没必要,selector 只是性能优化的一种方式——即一个线程管理海量连接,如果没有 select, 应用层无法用较低的成本处理海量连接,注意,不是不能处理,只是不能高效处理。

回过来,当 client socket 得到 server 的数据包,会进行解码反序列化,并唤醒阻塞在客户端的线程。从而完成一次调用。

线程模型

设计图:

image-20191029093524267

如上图所示。

在 client 端:

每个 Client 实例,维护一个 TCP 连接。该 Client 的写入方法是线程安全的。

当用户并发写入时,可并发写的同时并发回复,因为写和回复是异步的(此时可能会出现,线程 A 先 send ,线程 B 后 send,但由于网络延迟,B 先返回)。

在 server 端:

server 端维护着一个 ServerSocketChannel 实例,该实例的作用就是接收 accep 事件,且由一个线程维护这个 accept selector 。

当有新的 client 连接事件时,accept selector 就将这个连接“交给“ read 线程(默认 server 有 4 个 read 线程)。

什么是“交给”?

注意:每个 read 线程都维护着一个单独的 selector。 4 个 read 线程,就维护了 4 个 selector。

当 accept 得到新的客户端连接时,先从 4 个read 线程组里 get 一个线程,然后将这个 客户端连接 作为 key 注册到这个线程所对应的 read selector 上。从而将这个 Socket “交给” read 线程。

而这个 read 线程则使用这个 selector 轮询事件,如果 socket 可读,那么就进行读,读完之后,利用 DMA 写进磁盘。

RPC 协议

Server RPC 回复包协议

字段名称 字段长度(byte) 字段作用
magic_num 4 魔数校验,fast fail
version 1 rpc 协议版本
id 8 Request id, TCP 多路复用 id
length 8 rpc 实际消息内容的长度
Content length rpc 实际消息内容(JSON 序列化协议)

Client RPC 发送包协议

字段名称 字段长度(byte) 字段作用
magic_num 4 魔数校验,fast fail
id 8 Request id, TCP 多路复用 id, 默认 -1,表示不回复
nameContent 2 Request id, TCP 多路复用 id
bodyLength 8 rpc 实际消息内容的长度
nameContent bodyLength 文件名 UTF-8 数组

为什么 发送包和返回包协议不同?为了高效。

总结

注意:这是一个能用的,性能不错的,轻量的 SendFile 服务器实现,本地测试时, IO写盘达到 824MB/S,4c 4.2g inter i7 CPU 满载。

image-20191029120446781

代码地址:https://github.com/stateIs0/send_file

同时,欢迎大家 star, pr,issue。我来改进。

EOF

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,290评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,399评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,021评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,034评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,412评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,651评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,902评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,605评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,339评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,586评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,076评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,400评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,060评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,851评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,685评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,595评论 2 270

推荐阅读更多精彩内容