网络编程基础
OSI七层模型
应用层:http协议、文件服务器、邮件服务器
表示层:数据转换,解决不同系统的兼容问题
会话层:建立与应用程序的会话连接
传输层:提供端口号和传输协议(TCP、UDP)
以下三层多体现在硬件层面上
网络层:为我们的数据实现路由(路由器、交换机)
数据链路层:传输的地址的帧及错误的检测
物理层:以二进制形式,在物理机上实现数据传输(光纤、专线、各种物理介质实现)
网络编程 Socket
Socket技术
任何编程语言都支持socket(网络编程的技术)技术开发,目的就是解决两个应用程序通讯的问题
socket不属于某种协议,只是网络编程技术
Socket支持的协议TCP、UDP
TCP是一个面向连接可靠的协议,因为建立连接需要三次握手才可以实现数据传输,所以数据不会丢失。应用场景:http协议、rpc框架
UDP是面向无连接的协议,udp在通讯的时候不需要接受对方存在,属于不可靠传输,可能会存在数据丢包的问题。应用场景:即时通讯
三次握手,可靠建立连接
Syn(建立连接标记) Ack(确认标记)Fin(终止标记)
第一次握手:客户端向服务端发送消息(询问在不在)(消息为:syn=1 SEQ=X(随机数) )
第二次握手:服务器端收到客户端消息,给客户端发送消息(告诉对方我在)
第三次握手:客户端收到服务器回复后,再次给服务器发送消息(预发布数据,保障数据传输可靠性),之后建立连接
四次挥手,可靠关闭连接
第一次挥手:客户端向服务端发送释放连接通知;
第二次挥手:服务端收到通知后,通知客户端等待一下,可能存在数据未发送完毕,等待数据全部传输完毕后关闭连接;
第三次挥手:服务端所有数据发送完毕后,告诉客户端现在可以释放连接了
第四次挥手:客户端发出确人关闭通知,服务端收到后立即进入关闭状态。此时客户端进入时间等待状态,经过2**MSL(最长报文段寿命)时间后,撤销相应的TCB后 才进入关闭状态
http协议
http是一种超文本传输协议,基于tcp/ip协议完成的包装
默认端口80,明文传输
基于tcp包装(tcp3次握手和4次挥手)
Https协议,默认端口443,加密传输
特点
1.无状态,没有记忆的会话,如果需要则使用token、jwt、session等
2.请求(request)和响应模式(response)
3.简单快捷,灵活可以传输任何类型
请求:
请求头
请求类型
请求方法
响应:
响应头
响应体
版本
1.0:短连接,客户端与服务器端传输完成后,立马关闭连接。如果连接频繁,则非常消耗服务器资源;
1.1:长连接,客户端与服务器建立连接后,不会立马关闭,会保持一定的复用机制,默认300s空闲情况下自动断开连接
Linux IO模型
同步与异步区别
多线程角度:
同步:整个应用代码执行顺序从上到下执行,并返回结果;
异步:开启多个不同的分支实现并行执行,每个线程互不影响;
web项目角度:
http请求就是一个基于请求、响应的同步调用。如果响应慢的话,会导致用户一直等待;
如何解决等待问题,此时就可以考虑用多线程解决。
BIO(blocking I/O) 阻塞式IO(同步I/O)
不能够支持并发请求访问,可以多线程优化代码,但如果每个请求过来都使用一个线程,则非常消耗CPU资源。
当我们在调用io函数的时候,如果没有获取数据的情况下,就会一直阻塞。
NIO 非阻塞式IO(同步I/O)
不管是否获取到数据,都必须返回IO结果。如果没有获取数据结果的情况下,就使用循环控制不断重新获取。
IO复用(I/O multiplexing)(同步I/O)
网络IO,通过多个不同的tcp连接通信;
使用同一个线程合并处理多个不同的IO操作,减少cpu资源;
如redis原来,mysql连接原理;
信号驱动IO(singal driven I/O)(同步I/O)
发出一个请求实现观察监听,当有数据的时候直接异步回调;
如zookeeper节点事件通知;银行叫号;
异步I/O(asynchronous I/O)(异步I/O)
发出请求数据后,剩下的事情完全实现异步完成;
总结
IO操作
1.发起IO请求,等待数据准备
2.实际的IO操作,将数据从内核拷贝到进程中
BIO && 伪NIO
BIO:
优点:模型简单、编码简单;
缺点:性能瓶颈,请求量和线程数N:N关系;高并发下,cpu切换线程上下文损耗大;
伪NIO:使用线程池处理业务逻辑,控制线程数量;
案例:web服务器Tomcat7之前,都是使用BIO,7之后使用NIO
阻塞 && 非阻塞
指的是线程的状态
单个线程处理就是阻塞
多个线程处理就是非阻塞
阻塞与非阻塞区别在IO操作第一步
同步 && 异步
指的是消息的通知机制
同步IO 异步IO针对用户应用程序与内核的通知机制
同步与异步区别在IO操作第二步
同步、异步、阻塞、非阻塞
同步阻塞:洗衣机洗衣服,一直盯着直到洗完 去晒衣服
同步非阻塞:洗衣机洗衣服,同时去干别的时候,每隔一段时间就去看是否洗完 洗完去晒衣服
异步阻塞:不存在
异步非阻塞:洗衣机洗衣服,同时去干别的事情,洗完衣服后自动晒,等全部完成后来个音乐通知我
IO复用(select poll,epoll)
核心是可以同时处理多个connection,而不是更快,所以连接数不高的情况下,性能不一定比BIO(线程池实现伪NIO)好
多路复用模型中,每一个socket 设置为非阻塞 (对比上面的BIO 每个线程为非阻塞)
阻塞是被select函数阻塞,而不是被socket阻塞(上面例子都是单个socket)
netty
优势
netty是业界最流行的NIO框架,整合了多种协议(ftp、smtp、http等各种二进制文本协议)的实现经验,精心设计的框架
1.API使用简单
2.成熟稳定
3.社区活跃
4.经过大规模验证(互联网、大数据、网络游戏、电信通讯业)
哪些主流框架在用
1.搜索引擎框架 ElasticSearch
2.Hadopp子项目Avro项目底层通讯
3.阿里巴巴开源RPC框架 dubbo
版本
最稳定的版本,4.x,只要大版本一致就可以
IO多路复用
IO:网络IO
多路:多个tcp连接
复用:复用一个或几个线程
简单说:使用一个或几个线程处理多个TCP连接
优势:系统开销小,不必创建过过的进程/线程
select
原理:监视文件3类描述符,writefds、readfds、exceptfds
调用后select函数会阻塞住,等有数据 可读、可写、出异常 或者 超时 就会返回
select函数正常返回后,通过遍历fdset整个数组才能发现哪些句柄发生了事件,来找到就绪的描述符fd,然后进行对应的IO操作
几乎在所有的平台上支持,跨平台支持性好
缺点:
1.采用轮询方式扫码文件描述符,全部扫描,随着文件描述符FD数量增多而性能下降
2.每次调用select(),需要把fd集合从用户态拷贝到内核态,并进行遍历
3.最大的缺陷是单个进程打开的FD有限制,默认是1024(可修改宏定义,但效率仍然慢)
poll
select()和poll()系统调用的大体一样,处理多个描述符也是使用轮询的方式,根据描述符的状态,需要把fd集合从用户态拷贝到内核态,并进行遍历。
最大区别:poll没有最大文件描述符限制(使用链表的方式存储fd)
epoll
原理:在2.6内核中提出的,对比select 和 poll,epoll更加灵活,没有描述符限制,用户态拷贝到内核态只需要一次;使用事件通知,通过epoll_ctl注册fd,
一旦fd就绪,内核就会采用callback回调机制来激活对应的fd
优点:
1.没有fd限制,所支持的fd上限是操作系统的最大文件句柄数,1G内存 大概支持10万个句柄。
2.效率提高,使用回调通知而不是轮询的方式,不会随着fd数目的增加而效率下降
3.通过callback机制通知
linux内核核心函数:
1.epoll_create():在linux内核里面申请一个文件系统 B+树,返回epoll对象,也是一个fd
2.epoll_ctl():操作epoll对象,在这个对象里修改添加、修改、删除对应的链路fd,绑定一个callback函数
3.epoll_wait():判断并完成相应的IO操作
缺点:编程模型比select poll复杂
例子:100万个连接,里面有1万个连接是活跃,在 select、poll、epoll分别是怎样的表现
select:不修改宏定义,则需要 1000个进程才可以支持 100万连接
poll:100万个链接,遍历都响应不过来了,还有空间的拷贝消耗大量的资源
java IO演进
1.jdk1.4之前采用同步阻塞模型,也就是NIO
大型服务一般都采用C或者C++,因为可以直接操作系统提供的异步IO,AIO
2.jdk1.4推出NIO,同步非阻塞IO。jdk1.7升级,推出NIO2.0,提供AIO功能,支持文件和网络套接字的异步IO
线程模型和Reactor模式
设计模式——Reactor模式(反应器设计模式),是一种基于事件驱动的设计模式,在事件驱动的应用中,将一个或多个客户的服务请求分离(demultiplex)和调度(dispatch)给应用程序。在事件驱动的应用中,同步地、有序地处理同时接收的多个服务请求
一般出现在高并发系统中,比如Netty,Redis等
优点
1)响应快,不会因为单个同步而阻塞,虽然Reactor本身依然是同步的;
2)编程相对简单,最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进程的切换开销;
3)可扩展性,可以方便的通过增加Reactor实例个数来充分利用CPU资源;
缺点
1)相比传统的简单模型,Reactor增加了一定的复杂性,因而有一定的门槛,并且不易于调试。
2)Reactor模式需要系统底层的的支持,比如Java中的Selector支持,操作系统的select系统调用支持
Reactor模式基于事件驱动,适合处理海量的I/O事件,属于同步非阻塞IO(NIO)
Reactor单线程模型(比较少用)
内容:
1)作为NIO服务端,接收客户端的TCP连接;作为NIO客户端,向服务端发起TCP连接;
2)服务端读请求数据并响应;客户端写请求并读取响应
使用场景:
对应小业务则适合,编码简单;对于高负载、大并发的应用场景不适合,一个NIO线程处理太多请求,则负载过高,并且可能响应变慢,导致大量请求超时,而且万一线程挂了,则不可用了
Reactor多线程模型
内容:
1)一个Acceptor线程,一组NIO线程,一般是使用自带的线程池,包含一个任务队列和多个可用的线程
使用场景:
可满足大多数场景,但是当Acceptor需要做复杂操作的时候,比如认证等耗时操作,再高并发情况下则也会有性能问题
Reactor主从线程模型
内容:
1) Acceptor不在是一个线程,而是一组NIO线程;IO线程也是一组NIO线程,这样就是两个线程池去处理接入连接和处理IO
使用场景:
满足目前的大部分场景,也是Netty推荐使用的线程模型
为什么Netty使用NIO而不是AIO,是同步非阻塞还是异步非阻塞?
答案:
在Linux系统上,AIO的底层实现仍使用EPOLL,与NIO相同,因此在性能上没有明显的优势
Netty整体架构是reactor模型,采用epoll机制,所以往深的说,还是IO多路复用模式,所以也可说netty是同步非阻塞模型(看的层次不一样)
很多人说这是netty是基于Java NIO 类库实现的异步通讯框架
特点:异步非阻塞、基于事件驱动,性能高,高可靠性和高可定制性。