1、SDK与API的区别?
SDK是Software Development Kit的缩写,即软件开发工具包。可以把SDK想象成一个虚拟的程序包,在这个程序包中有一份做好的软件功能,这份程序包几乎是全封闭的,通过接口联通外界,相应的接口就是API。
JDK(Java Development Kit),即Java开发工具包,是针对Java开发者的产品,JDK是SDK的一个子集,JDK已经成为使用最广泛的JAVA SDK 。JDK是整个Java的核心,包括Java运行环境JRE、一堆Java工具(javac/java/jdb等)和java基础的类库(Java API、tr.jar等)。
JAR是java的归档格式,是一个程序集,内部全是class文件,是java SDK的一种打包形式。因为SDK不限语言,不限平台,所以针对不同语言、平台都有不同的打包方式。
2、长连接与短连接?
HTTP的长连接和短连接本质上是TCP长连接和短连接。HTTP属于应用层协议,在传输层使用TCP协议,在网络层使用IP协议。 IP协议主要解决网络路由和寻址问题,TCP协议主要解决如何在IP层之上可靠地传递数据包,使得网络上接收端收到发送端所发出的所有包,并且顺序与发送顺序一致,TCP协议是可靠的、面向连接的。
【TCP短连接】:client向server发起连接请求,server接到请求,然后双方建立连接。client向server发送消息,server回应client,然后一次请求就完成了。这时候双方任意都可以发起close操作,不过一般都是client先发起close操作。上述可知,短连接一般只会在 client/server间传递一次请求操作。
【TCP长连接】:client向server发起连接,server接受client连接,双方建立连接,client与server完成一次请求后,它们之间的连接并不会主动关闭,后续的读写操作会继续使用这个连接。TCP长连接支持Keepalive保活功能,如果客户端已经消失而连接未断开,则会使得服务器上保留一个半开放的连接,而服务器又在等待来自客户端的数据,此时服务器将永远等待客户端的数据,保活功能就是试图在服务端器端检测到这种半开放的连接。
3、中介者模式
中介者模式(Mediator Pattern):定义一个中介对象来封装系列对象之间的交互。中介者使各个对象不需要显示地相互引用,从而使其耦合性松散,而且可以独立地改变他们之间的交互。
4、日常开发中对MVC的误解
MVC代表着Model、View、Controller,View层是界面,Controller层是业务逻辑,Model层是数据库访问。View通常不会直接提交数据给Model,它会先把数据提交给Controller,然后Controller再将数据转发给Model。假如此时程序业务逻辑的处理方式有变化,那么只需要在Controller中将原来的Model换成新实现的Model就可以了,控制器的作用就是这么简单, 用来将不同的View和不同的Model组织在一起,Controller实际相当于一个中介者。
我们在实际的开发过程中,经常会将MVC和另一种软件开发模式“三层架构”相混淆,“三层架构”分别是UI层表示用户界面,BLL层表示业务逻辑,DAL层表示数据访问,当前我们开发大部分使用的都是“三层架构”的模式,却经常误解成MVC。
5、Tomcat和Spring的关系
Tomcat是HTTP服务器、Servlet容器,负责给Servlet提供一个运行的环境,可以把HTTP服务器想象成前台的接待,负责网络通信和解析请求,Servlet容器是业务部门,负责处理业务请求。Tomcat是实现了Servlet规范的Servlet容器,SpringMVC是处理Servlet请求的应用,其中DispatcherServlet实现了Servlet接口,Tomcat负责加载和调用DispatcherServlet。同时,DispatcherServlet有自己的容器(SpringMVC)容器,这个容器负责管理SpringMVC相关的bean,比如Controler和ViewResolver等。同时,Spring中还有其他的Bean比如Service和DAO等,这些由全局的Spring IOC容器管理,因此,Spring有两个IOC容器。Tomcat能够支持的并发连接数是1000左右。
6、PageCache
pageCache页高速缓冲存储器,简称页高缓,用于缓存文件的页数据,从磁盘中读取到的内容就是存储在pageCache里的。
在Linux的实现中,文件Cache分为两个层面,一是PageCache,另一个是BufferCache(块缓存)。PageCache用于缓存文件的页数据,大小通常为4K;BufferCache用于缓存块设备(如磁盘)的块数据,大小通常为1K。
在Linux2.4版本的内核之前,PageCache和BufferCache是完全分离的。但是块设备大多数是磁盘,磁盘上的数据又大多通过文件系统来组织,这种设计导致很多数据被缓存了两次,浪费内存空间。所以在2.4版本内核之后,两块内存近似融合在了一起,如果一个文件的页加载到了PageCache,那么BufferCache只需要维护块指向页的指针。在2.6版本内核中,PageCache和BufferCache进一步结合。每一个PageCache包含若干BufferCache。
【读cache】
当内核发起一个读请求时(例如进程发起read()请求),首先会检查请求的数据是否缓存到了PageCache中。如果有,那么直接从内存中读取,不需要访问磁盘,这被称为cache命中(cache hit)。如果cache中没有请求的数据,即cache未命中(cache miss),就必须从磁盘中读取数据。然后内核将读取的数据缓存到cache中,这样后续的读请求就可以命中cache了。
【写cache】
当内核发起一个写请求时(例如进程发起write()请求),直接往cache中写入。内核会将被写入的page标记为dirty,并将其加入dirty list中。内核会周期性地将dirty list中的page写回到磁盘上,从而使磁盘上的数据和内存中缓存的数据一致。
7、ZeroCopy-零拷贝
【DMA技术】:DMA全称Direct Memory Access直接内存访问技术,指的是在进行I/O设备或内存数据传输时,数据的搬运工作都交给DMA控制器处理,CPU不参与数据搬运工作,进而提高CPU利用率,各种I/O设备都会自带DMA控制器。
【传统文件传输】:服务器如果需要进行文件传输,首先需要读取磁盘文件,然后通过网络协议传输给客户端。由于用户态没有操作磁盘和网卡的权限,需要切换为内核态才能操作,所以在传统的文件传输过程中,数据的读取和写入都需要在用户空间和内核空间来回复制。一次上线文切换需要耗时几十纳秒到几微妙,在高并发场景下会影响系统性能。
传统文件传输流程:
- 磁盘数据(通常会先缓存到磁盘控制器缓冲区)拷贝到操作系统内核缓冲区。
- 操作系统内核缓冲区数据拷贝到用户缓冲区
- 用户缓冲区数据拷贝到socket缓冲区
- socket缓冲区数据拷贝到网卡缓冲区
这个过程中发生了4次上下文切换(从磁盘读取数据时2次,写数据到网卡时2次),和4次数据拷贝。在没有DMA技术前,从磁盘到内核缓存、从Socket缓存到网卡缓存,的数据传输也是由CPU处理的,在数据传输期间CPU无法执行其他任务,同时这些外设的传输效率比起内存传输效率低很多,会严重影响CPU利用率,所以才出现了DMA技术,外设和系统缓冲区间的数据传输由设备自带的DMA控制器自行处理,处理完成后在发送中断信号给CPU,CPU再完成用户空间和内核空间的数据传输。
【零拷贝】:零拷贝应用了内存映射技术mmap,它的核心是操作系统把内核缓冲区与应用程序共享,将一段用户空间内存映射到内核空间,当映射成功后,用户对这段内存区域的修改可以直接反映到内核空间;同样地,内核空间对这段区域的修改也直接反映用户空间。正因为有这样的映射关系, 就不需要在用户态与内核态之间拷贝数据, 提高了数据传输的效率,这就是内存直接映射技术。
有了mmap后,数据无需再传输到用户空间,节省了两2次CPU拷贝,但是仍需要4次上下文切换,因为read(从磁盘读数据)和write(写数据到网卡)两次系统调用的发起都需上下文切换,同时内核缓冲区到socket缓冲区的数据传输,还是需要一次CPU拷贝。
为了进一步减少上线文切换次数,read、write两次系统调用被合并成一次sendfile,同时为了将CPU拷贝次数减少到0,又推出了带DMA收集拷贝功能的sendfile,这才真正意义上的实现了“零拷贝”(这个“零拷贝”实际指的是零CPU拷贝):
- 用户应用程序发出sendfile系统调用,上下文从用户态切换到内核态,然后通过DMA控制器将数据从磁盘中复制到内核缓冲区中
- 接下来不需要CPU将数据复制到socket缓冲区,而是将相应的文件描述符信息复制到 socket 缓冲区,该描述符包含了两种的信息:①内核缓冲区的内存地址、②内核缓冲区的偏移量
- sendfile系统调用返回,上下文从内核态切换到用户态
- DMA根据socket缓冲区中描述符提供的地址和偏移量直接将内核缓冲区中的数据复制到网卡
零拷贝极大地提升了文件传输效率,在Java NIO、Netty框架、kafka中都有应用,但是因为数据实际并没有进入用户空间,所以零拷贝不支持进程对文件内容作一些加工再发送,比如数据压缩后再发送。
8、fsync
当你想将数据write进文件时,内核通常会将该数据复制到其中一个缓冲区中,如果该缓冲没被写满的话,内核就不会把它放入到输出队列中。当这个缓冲区被写满或者内核想重用这个缓冲区时,才会将其排到输出队列中。等它到达等待队列首部时才会进行实际的IO操作。这里的输出方式就是大家耳熟能详的:延迟写,这个缓冲区就是大家耳熟能详的:OS Cache。很明显、延迟写降低了磁盘读写的次数,但同时也降低了文件的更新速度。这样当OS Crash时由于这种延迟写的机制可能会造成文件更新内容的丢失。而为了保证磁盘上的实际文件和缓冲区中的内容保持一致,UNIX系统提供了三个系统调用:sync、fsync、fdatasync。
-
sync系统调用:将所有修改过的缓冲区排入写队列,然后就返回了,它并不等实际的写磁盘的操作结束。所以它的返回并不能保证数据的安全性。通常会有一个update系统守护进程每隔30s调用一次sync。
-
fsync系统调用:需要你在入参的位置上传递给他一个fd,然后系统调用就会对这个fd指向的文件起作用。fsync会确保一直到写磁盘操作结束才会返回,所以当你的程序使用这个函数并且它成功返回时,就说明数据肯定已经安全的落盘了。所以fsync适合数据库这种程序。
-
fdatasync系统调用:和fsync类似但是它只会影响文件的一部分,因为除了文件中的数据之外,fsync还会同步文件的属性。
9、Nginx、LVS、HAProxy
在实际的APP应用程序中,我们知道Web服务群集前总是有负载平衡服务,典型的负载平衡软件使用会根据网站规模的增加而使用不同的技术。 例如,中小型WEB APP应用程序Nginx就足够了,但对于大型站点和关键服务可以使用LVS。Nginx、LVS、HAProxy是目前使用最广泛的三种负载平衡软件。
【Nginx】 是高性能的Web和反向代理服务器,Nginx是三种负载均衡软件中最容易使用、适用性最广的软件,主要是七层网络通信模型中的第七层APP应用。Nginx内存消耗少:3万个连接,打开10个Nginx进程,只占用150M内存(15M*10=150M )。
【HAProxy】 是一款以c语言编写的自由开放源代码软件,提供高可用性、负载平衡、基于TCP (第4层)和HTTP (第7层)的APP应用程序代理。 特别适用于负荷较高的网站。 这些站点通常需要保持会话或处理7个阶段。不同于Nginx可以作为WEB服务器,HAProxy本身仅仅就只是一款负载均衡软件,单纯从效率上来讲HAProxy更会比Nginx有更出色的负载均衡速度,在并发处理上也是优于Nginx的。