名字
感谢开源,最近理解tio时写的一些思考,如有不对请指正。
浅谈在线聊天技术原理
简单来说,在线聊天就是一个服务端开启端口,然后由很多个客户端进行连接,然后由服务端将信息转发给各个客户端,于是就有了几个基础的东西(暂不考虑业务逻辑)
1.开启端口
2.进行连接
3.谁发来的
4.查看发给谁的
5.转发给他
其中主要处理的内容是客户端相服务端发送数据,服务端向客户端发送数据,所以其中最重要的就是字符(或者说流)传输。
一.理解流的概念:
在一般的认知中,本地文件与网络文件是两回事,本地文件打开速度快,网络文件需要网速下载,好像是本地和外地的概念。
但是这里是根据读取速度来判断的,假设这样一个场景,5G兴起之后网速非常快,从网络上读取速度比硬盘读取的速度还要快,那么硬盘中的文件还是本地文件吗?
其实对于计算机内核来说,硬盘和网络中存放的文件都是外地文件,只有在它内存中才是可以直接操作的内部文件但这种放到内存中的文件叫做流。
二.理解socket输入输出和流
可能把打字发送的过程理解和读取网络上的文件当成一回事不太好理解。先来解释一下:文件其实就是一堆数据的组合,那么当客户端写完内容给服务端发送信息也是传输堆数据的组合,所以我们可以将它认知为一种概念。
加深理解就是,我现在编辑WORD只是文字而已,和网络中准备数据一样,然后我点击保存这样一个文件就出现了,我点击保存的过程中也可以理解客户端向服务端发送数据的过程。
在这个例子中有个容易忽视的点,就是键盘输入和网络输入对于计算机内核来说是同一回事.键盘接口是USB接口,键盘通过USB接口写入内存,网络请求是网卡端口,客户端将信息通过网络写到内存中.
所以IO包含了硬盘文件,网络传输,键盘传输
这就相当于:你借给别人的钱,投资股市的钱,工资的收入 都是你的钱 但是你可以直接支配的只有你银行卡中的钱,假如你想使用就需要先将这些通过一些方式转到自己的银行卡上才能直接使用。本来借给别人的钱我想类比支付包的钱,但是想到了大家都用支付宝直接支付0.0。
好的 这样就对基础的网络连接有了了解了,接下来我们理解Socket的效率问题,这个理解很重要,不然没法把IO的模式跟网络请求的效率联系到一起。
三.BIO,NIO,AIO
这个有点不好理解,先来讲个故事吧。
老罗开了一家公司(就是罗永浩,最近直播卖货还钱那家伙,我是他粉丝),这家公司有一项业务是租房子,租给一些商用大厦,然后给这个大厦配了一个公司员工进行管理这个大厦。因为是大厦所以事情比较多,每个人员还是很忙碌的,租金也收的不错,老罗还挺满意。(BIO方式租房)
有一天来了一个人,说我要租房子,老罗说好,然后这人说我不要租这么大的我只要租个六十平方的用来自住租金要按平米计算。老罗不想租,但是法律规定不可以不租给有需求的人,而且公司还有闲置人员所以就租了。后来又越来越多的这样的人租六十平方的房子,然后员工很快就分配完了。再来人的话,老罗虽然还有很多房子可以用来租住但是没有人员进行管理了。然后老罗就想了一下,每个地方放一个员工,员工工资五千,每月收一次房租八百。好像哪里不对吧,得想办法解决。(BIO方式租房遇见的瓶颈)
然后老罗想了个办法,将员工全都召回来,将每一个租房的人都记录一下地址和个人信息,然后交给一个员工进行管理,再来人租房就登记一下,员工的工作就是每天查询今天谁要交房租,然后去上门收费。(NIO模式租房)
这样之后很久也没有发现有什么问题,后来租房越来越多的时候,老罗发现收到的钱不够,实际收到的钱比租出去的房子应该收的钱少很多,而且差值越来越大,然后就怀疑员工是不是私吞公款。叫来员工问他,员工说,我没有 我只是每天工作八小时,上班去收钱,下班回家,但是人太多了,我收不完。但是第二天还有第二天需要收的钱就总是干不完了。老罗经过调查后发现果然如此。得想办法解决。(NIO的瓶颈)
他开会讨论问怎么解决,然后有人提出说,我们多派出几个人进行管理吧,每个人负责一片区域,这样就能忙得过来了,老罗觉得可行。有个人忽然站起来说,我们直接给用户断电吧,我们知道用户的租住时间,我们把每天该缴费的租客直接停电,然后顾客就知道自己到交租时间了,他们就会自己过来交钱了。老罗一拍桌子,好主意!(AIO模式租房)
后来,因为老罗这种做法太不人道,租客都不干了,都搬走了,老罗公司破产企业倒闭。(没啥意思,故事讲上瘾了,对应现实,哈哈)
故事讲完了,开始讲技术:
BIO:同步阻塞式IO
NIO:同步非阻塞式IO
AIO:异步非阻塞式IO
阻塞概念:在故事中就是要不要有人等,第一种就是一直在那里等着来处理事情,非阻塞就是管理人员做完事情了就走。
对于IO来说就是服务端要不要等待客户端准备数据的过程。
异步概念:在故事中就是,公司人员找租客还是租客主动找公司的过程。
对于IO就是客户端通知服务端还是服务端通知客户端
虽然现实生活中这个类比很好理解,但是在计算机中是真的难理解。
顺便一提多线程和单线程的概念我在故事中没有体现:
如果公司只管理一个大厦那就算是单线程的,但一般公司(CPU)都不止是一个人(线程),一个人叫个体工商户。至于能有多少人就看公司有多大,财力有多雄厚(CPU处理能力)。
换个例子,对接一个人聊天是单线程,对接多个人同时进行聊天就是多线程。
四.简单原理就这些了,看热闹的差不多就到这了,接下来是应用程序和系统程序原理。
理解JAVA的nio包(详情请看nio的理解文档,这里只是了解)
• Channel
• Buffer
• Selector
1.理解Channel
我原先对chanle的理解来自于,电视的频道,一个频道对应一个正在播放的节目,一种映射关系,但是实际上是不完全的,它还有管道的意思。于是我们就可以把管道,给设置一个key(句柄)来使用它。
2.理解Buffer
• capacity
• position
• limit
其中 position 和 limit 的含义与 Buffer 处于读模式或写模式有关, 而 capacity 的含义与 Buffer 所处的模式无关.
Capacity
一个内存块会有一个固定的大小, 即容量(capacity), 我们最多写入capacity 个单位的数据到 Buffer 中, 例如一个 DoubleBuffer, 其 Capacity 是100, 那么我们最多可以写入100个 double 数据.
Position
当从一个 Buffer 中写入数据时, 我们是从 Buffer 的一个确定的位置(position)开始写入的. 在最初的状态时, position 的值是0. 每当我们写入了一个单位的数据后, position 就会递增一.
当我们从 Buffer 中读取数据时, 我们也是从某个特定的位置开始读取的. 当我们调用了 filp()方法将 Buffer 从写模式转换到读模式时, position 的值会自动被设置为0, 每当我们读取一个单位的数据, position 的值递增1.
position 表示了读写操作的位置指针.
limit - position 表示此时还可以写入/读取多少单位的数据.
3.理解Sector
为了使用 Selector, 我们首先需要将 Channel 注册到 Selector 中, 随后调用 Selector 的 select()方法, 这个方法会阻塞, 直到注册在 Selector 中的 Channel 发送可读写事件. 当这个方法返回后, 当前的这个线程就可以处理 Channel 的事件了.
五.最后系统的实现(详情看文件多路复用和epoll实现文件)
1.一切皆文件,数据与逻辑
就算理解了上面的东西,还有一点不好说明的东西就是异步的实现
就是如何让它来通知我。
我们的文件都有一些文件描述,这些是对文件的描述但并不是文件本身(描述可以用来形容文件,但不包含文件内部内容,这通常来说文件描述要比文件小的多),当CPU把它读成流的时候,依然也有对文件进行的描述FileDescriptor
因为我们把网络通信也看成了文件,所以它也有文件描述,在我们的故事中将它理解为了用户信息。(李姐万岁)
这里用linux2.6内核版本的epoll实现来说明;
这里要理解一个概念,就是数据和逻辑并没有严格的界限的,对于计算机来说都是01的数据组合而已。(这点很重要)
于是就有了这样的一个使用方式,当数据发生变化时自动触发逻辑,开始执行逻辑代码,也就是故事中说的断电以后,用户自己过来缴费的行为。
想了半天类比看这张图可能比较合适
上面的水流到下面,当水足够多时因为水的势能竹子状态会发生改变,当状态改变时会把水倒出去,失去了水的势能的竹子会恢复原位。(很贴切啊哈哈哈)
这里有多路复用的概念,详细请查看原文档。
其实类比的有数据库的触发器,当某字段发生改变的时候可以通过编写触发器调用一些逻辑代码,有点类似但我觉得不一样,因为数据库字段变动是通过数据操作的逻辑代码进行触发的应该不是一回事,这里没有对这种实现方式进行剖析就不深入谈了。
具体实现
epoll通过在linux内核中申请一个简易的文件系统(文件系统一般用什么数据结构实现?B+树)。把原先的select/poll调用分成了3个部分:
1)调用epoll_create()建立一个epoll对象(在epoll文件系统中为这个句柄对象分配资源)
2)调用epoll_ctl向epoll对象中添加这100万个连接的套接字
3)调用epoll_wait收集发生的事件的连接
其中涉及到的数据结构:
epoll用kmem_cache_create(slab分配器)分配内存用来存放struct epitem和struct eppoll_entry。
当向系统中添加一个fd时,就创建一个epitem结构体,这是内核管理epoll的基本数据结构:
当调用epoll_wait检查是否有事件发生时,只需要检查eventpoll对象中的rdlist双链表中是否有epitem元素即可。如果rdlist不为空,则把发生的事件复制到用户态,同时将事件数量返回给用户。
最新评论 我的评论
t-io为本站提供HTTP、WebSocket、Socket、页面渲染与压缩等服务,nginx为本站提供反向代理服务
© 2017-2021 钛特云 版权所有 | 浙ICP备17032976号 | 浙公网安备 33011802002129号