做为一名后端屌丝程序员,对算法、并发、性能乐此不疲。可是,随着年龄和阅历的增长,显然叶落而不知秋的心态是不太能混了。尤为是,某T面试官在明知我是后端,且明确表示对HTTP协议不太熟的状况下,强行让我解释HTTP状态码200至600的含义。这,即是本篇的初衷,讲一讲后端眼里的前端故事。内容基于《深刻分析JavaWeb技术内幕》,加入了本身的理解,思惟会比较跳跃,须要后端基础。html
<h4 id=0><font color=red size=3 face="微软雅黑"> web请求过程</font></h4>前端
做为一名从CS架构转入BS架构的后端,不得不说,BS是趋势也是方向,两个好处:java
- 客户端使用统一的浏览器,使用配置简单
- 服务端基于统一的HTTP,简化开发模式,而且开源服务器众多,开发成本低
<h4 id=1><font color=red size=3 face="微软雅黑"> B/S网络架构</font></h4> CS架构一般是长连接交互数据,而BS架构是经过基于HTTP协议的无状态的短连接通信。当在浏览器输入`http://www.cnblogs.com/1024Community/`时,会发生一系列动做,最终将须要的信息返回到浏览器。其中涉及不少概念,如DNS解析、CDN、负载均衡等(后续会详解),可是基本架构仍是差很少,基本流程是:linux
浏览器输入URL---DNS解析成对应IP---负载均衡服务器---CDN---服务端系统---分布式缓存---文件系统/DB
程序员
包含CDN的架构图:web
<h4 id=2><font color=red size=3 face="微软雅黑"> 发起一个HTTP请求</font></h4> 归纳为,经过URL解析到服务器IP,创建socket连接,发送符合HTTP规范的输入流,等待服务器返回数据,而后断开连接。linux中curl+URL命令,能够简单发起一个HTTP请求。面试
<h4 id=3><font color=red size=3 face="微软雅黑"> HTTP解析</font></h4> HTTP是BS网络架构的核心,程序员*杀人放火*之必备。 首先,要了解的是HTTP header部分,它控制着数据传输、浏览器渲染、服务器执行逻辑。 常见HTTP请求头: 算法
常见响应头: 后端
常见错误码: 设计模式
总结:
HTTP请求头,charset、encoding、language、host、User-Agent、Connection HTTP响应头,Server、type、encoding、language、length、Keep-Alive HTTP常见状态码,200成功,302临时跳转,400请求语法错误,403服务器收到请求,拒绝服务,404请求资源不存在,500服务端异常
<h4 id=4><font color=red size=3 face="微软雅黑"> 浏览器缓存机制</font></h4> 复杂而又重要,经常使用ctrl+F5组合键刷新界面,来得到最新数据。其原理是,在header中添加了两项,Pragma:no-cache和Cache-Control:no-cache。对于缓存相关的header参数,主要以下:
Pragma/Cache-Control 缓存控制,Cache-Control优先级较高,和其余请求字段同时存在时(如Expires),会覆盖其余字段。Pragma相似,经常使用就是Pragma:no-cache,可选值以下:
Expires 指定一个日期,超过这个日期,缓存超时,从新请求
Last-Modified/Etag 服务端响应头中返回一个Last-Modified字段,告诉浏览器这个页面的最后修改时间。当浏览器再次请求时,在header中添加If-Modified-Since
字段,来询问页面是否最新,最新则返回304,服务器也不会传送数据。 Etag相似功能,为每个页面分配一个惟一编号,后端比较难处理,须要记住网站全部资源。
<h4 id=5><font color=red size=3 face="微软雅黑"> DNS域名解析</font></h4> 将域名解析成地址,很是重要。整个HTTP通讯都是基于TCP/IP协议簇的,没有IP地址就无法通讯,可是IP地址是一串数字,难记,因此有了IP和域名的对应关系。有了对应关系,就要有解析,知道域名经过对应关系,就能获得IP,从而创建起通讯。
<h4 id=6><font color=red size=3 face="微软雅黑"> DNS域名解析过程</font></h4> 看书说话,咱们来捋一捋,当你在浏览器输入一串URL时,怎么找到的对应服务器IP 
Tips: win下使用nslookup,linux使用dig查询解析过程。 两个缓存地方LDS和本地机器,可用命令刷新,JVM也会缓存DNS结果,使用InetAddress时,注意使用单例模式,避免性能问题。
<h4 id=7><font color=red size=3 face="微软雅黑"> CDN工做机制</font></h4> Content Delivery Network(内容分布网络)相似镜像+缓存+总体LB,一般缓存静态资源,用户主站取动态数据,CDN下载静态数据。目标:可扩展,安全性,可靠性。
<h4 id=8><font color=red size=3 face="微软雅黑"> CDN架构</font></h4>
看图说话,经过域名解析,一般会CNAME到CDN全局中的DNS负载均衡服务器,在经过这个GTM(广域网流量管理)分配到离用户最近的CDN节点。用户就能够到这个节点访问静态文件了。
<h4 id=9><font color=red size=3 face="微软雅黑"> 负载均衡</font></h4> 负载均衡,分为链路LB(DNS解析成不一样IP)、集群LB(分为硬件和软件,硬件贵,性能好,但不能动态扩容,如F5;软件LB,如LVS)、操做系统LB(软中断和硬件中断,如多队列网卡)。
<h4 id=10><font color=red size=3 face="微软雅黑"> CDN动态加速</font></h4> `总结为,经过CDN的DNS解析中经过动态的链路探测寻找回源最佳路径,而后经过DNS调度将全部请求调度到选定的路径上回源,加速用户访问效率`
<h4 id=11><font color=red size=3 face="微软雅黑"> Java I/O的工做机制</font></h4> <h4 id=12><font color=red size=3 face="微软雅黑"> Java I/O类库的基本架构</font></h4> IO问题是人机交互的核心问题,是获取和交换信息的渠道,向来是应用系统的瓶颈,Java也在持续优化,如1.4版本引入了NIO。整个java.io包几十个类,有些概念再也不详细介绍了(如字节流、字符流、io包继承结构),整体上看IO大致分为: 1. 基于字节操做的io接口:InputStream和OutputStream 2. 基于字符操做的io接口:Writer和Reader 3. 基于磁盘操做的io接口:File 4. 基于网络操做的io接口:Socket(也算是IO)
<h4 id=13><font color=red size=3 face="微软雅黑"> 字节和字符的转化</font></h4> 首先,数据持久化或网络传输都是以字节进行的,这就涉及一个字符和字节转化的问题,其中最重要的就是字符集编码问题,不然很容易出现常见的乱码问题,字符解码类图以下:
其中InputStreamReader类是从字节到字符转化桥梁,过程当中要指定字符集,不然使用操做系统默认。 补充个UML知识,上图中使用的是组合,是总体与部分的关系,代码实现就是成员变量,以下
读的时候,字节流转化为字符,要解码(StreamDecoder),一样道理,写入是从字符到字节,须要编码(StreamEncoder)
<h4 id=14><font color=red size=3 face="微软雅黑"> 磁盘IO工做机制</font></h4> <h4 id=15><font color=red size=3 face="微软雅黑"> 几种访问文件方式</font></h4> 访问文件,读取和写入是调用操做系统的IO接口,由于磁盘是由操做系统管理。而只要是系统调用就会存在内核空间和用户空间切换问题,这自己是操做系统保护自己运行安全的机制,这就会出现一个问题,`内核空间和用户空间的数据复制`,因为内核空间访问快,用户空间访问慢,因此,出现了缓存。这是基本原理,几种访问文件方式以下: 1. 标准访问文件方式(实际上读写都是和内核空间缓存打交道,何时刷新到磁盘由操做系统决定) 2. 直接IO方式(内核不缓存,直接和磁盘IO打交道,慢,一般结合异步IO提高性能) 3. 同步访问文件(和标准访问相似,可是等缓存数据刷新到磁盘,才返回给应用) 4. 异步访问方式(访问发出后,不阻塞,异步返回) 5. 内存映射方式(把内核空间和磁盘文件关联,共享数据,减小`数据复制`操做)
<h4 id=16><font color=red size=3 face="微软雅黑"> Java访问磁盘文件</font></h4> File对象是个虚拟的描述,解码类StreamDecoder、真正的文件描述类FileDescriptor,一图胜万言,以下,
<h4 id=17><font color=red size=3 face="微软雅黑"> Java序列化技术</font></h4> 序列化就是将一个对象转为为一串二进制的字节数组,经过保存或转移这些字节数据来达到持久化的目的。接口,java.io.Serializable。 对象序列化以后,查看二进制数组,会包含序列化协议、版本、是否新对象、class完整类名、UID、标记、所含域的个数、域类型、域名城、父类信息、类各属性的实际值。 几个Java序列化要点: 1. 父类继承Serializable接口时,全部子类可序列化 2. 子类实现Serializable接口时,父类没有,则只有字类的属性可序列化(不报错) 3. 若是序列化的属性是对象,则这个对象也必须实现Serializable接口,不然报错 4. 反序列化时,若是对象属性有修改或删减,修改的属性部分会丢失,不报错 5. 反序列化时,UID被修改,会失败
纯Java环境,序列化能够很好的工做。多语言环境见仍是推荐使用通用的数据结构如XML、JSON。
<h4 id=18><font color=red size=3 face="微软雅黑"> 网络IO机制</font></h4> <h4 id=19><font color=red size=3 face="微软雅黑"> TCP三次握手和四次挥手</font></h4> TCP创建过程,三次握手和四次挥手,过程以下:
两个问题:
为何A还要发送一次确认呢?能够二次握手吗? 为了防止已失效的链接请求报文段忽然又传送到了B,于是产生错误
为何链接的时候是三次握手,关闭的时候倒是四次握手? 由于当Server端收到Client端的SYN链接请求报文后,能够直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。可是关闭链接时,当Server端收到FIN报文时,极可能并不会当即关闭SOCKET,因此只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端全部的报文都发送完了,我才能发送FIN报文,所以不能一块儿发送。故须要四步握手。
<h4 id=20><font color=red size=3 face="微软雅黑"> Java Socket工做机制</font></h4> 底层TCP/IP协议,Socket和ServerSocket(accept以前是阻塞的)创建链接,经过字节流传输,传输过程当中,操做系统会为InputStream和OutputStream分配必定大小缓冲区,数据读写经过缓冲区完成。
<h4 id=21><font color=red size=3 face="微软雅黑"> NIO概述</font></h4> BIO方式,无论是网络仍是磁盘,一旦有阻塞,线程都会失去CPU使用权。当须要大量HTTP长连接的状况,或者提高个别IO请求优先级,或者竞态资源同步,BIO处理起来会很是复杂,此时,NIO闪亮登场。显然,这个话题须要一个崭新的大篇幅来介绍,此处省略一万字=。=
<h4 id=22><font color=red size=3 face="微软雅黑"> IO调优</font></h4> - 磁盘IO优化
- 增长缓存,减小磁盘访问次数 - 优化磁盘管理系统,设计最优的磁盘寻址策略(太底层) - 设计合理的磁盘存储数据块 - 应用合理的RAID策略提高磁盘IO
- 32位系统一般只有65535个端口,0~1024受保护,查看可用端口数量,较少时能够经过更改tcp_fin_timeout位更小的值来快速释放。经常使用信息: cat /proc/net/netstat :查看TCP统计信息 cat /proc/net/snmp :查看当前系统链接状况 netstat -s :查看网络统计信息
- 减小网络交互次数 - 减小网络传输数据量大小 - 减小编码(重要,网络流是字节形式,字符转字节比较耗时,尽可能以字节形式传输或提早转码) - 同步和异步的选择(同步就是一个任务的完成须要依赖另一个任务的完成) - 阻塞与非阻塞的选择(阻塞就是CPU停下来等待一个慢的操做完成才接着完成其余的工做) - 同步、异步、阻塞、非阻塞混搭组合(根据场景来选择)
<h4 id=23><font color=red size=3 face="微软雅黑"> IO涉及的设计模式</font></h4> *如下高能,须要功底:*
适配器模式,关键点:Adapter完成源到目标的适配,通常是继承源或持有源(构造器注入、方法注入等),实现目标接口。 装饰器模式,关键点:在不改变源的接口的状况下,进行功能扩展。io包InputStream各类装饰器(FilterInputStream、BufferedInputStream等)经过持有源(构造器注入)来完成功能扩展。
二者都对类进行了包装,二者的本质区别在于是否改变了源的接口
。
以上来自天团运营总监:坤少