突击笔试面试(2)

HTTP和HTTPS
http超文本控制协议主要在Web浏览器和网站服务器之间传递信息 HTTP协议以明文方式发送内容,所以数据不安全
https可以说成是安全版的http,即在hhtp中加入ssl层。能保证数据传输的安全、确认网站的真实性。http的端口是80,https是443。http是无状态协议
SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。
SSL 证书就是遵守 SSL 协议,由受信任的数字证书颁发机构 CA,在验证服务器身份后颁发,具有服务器身份验证和数据传输加密功能。

HTTP请求过程
建立连接完毕以后客户端会发送请求给服务端
服务端接受请求并且做出响应发送给客户端
客户端收到响应并且解析响应响应给客户

1.长连接
Client方与Server方先建立通讯连接,连接建立后 不断开, 然后再进行报文发送和接收。
2.短连接
Client方与Server每进行一次报文收发交易时才进行通讯连接,交易完毕后立即断开连接。此种方式常用于一点对多点通讯,比如多个Client连接一个Server.

长连接与短连接的概念:前者是整个通讯过程,客户端和服务端只用一个Socket对象,长期保持Socket的连接;后者是每次请求,都新建一个Socket,处理完一个请求就直接关闭掉Socket。
所以,其实区分长短连接就是:整个客户和服务端的通讯过程是利用一个Socket还是多个Socket进行的。

HTTPS请求过程
客户端发送请求到服务端
服务端返回公钥和证书到客户端
客户端接收后会验证证书的安全性,如果通过则会随机生成一个随机数,用公钥对其加密,发送到服务端
服务端接受到这个加密后的随机数后会用私钥对其解密得到真正的随机数,随后用这个随机数当做私钥对需要发送的数据进行对称加密
客户端在接收到加密后的数据使用私钥(即生成的随机值)对数据进行解密并且解析数据呈现结果给客户

SSL加密建立
TCP和UDP的区别
1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,(连接是对状态的保持,就是在客户端和服务器端都维护一个变量,这个变量维护现在数据传输的状态,
序列号和应答号是TCP通讯特有的参数,TCP通讯利用序列号和应答号来保持和确认数据的关联与正确性)
2、通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP不保证可靠交付
3、TCP面向字节流UDP是面向报文的
UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5、TCP速度慢,UDP速度快

TCP应用场景
当对网络通信质量有要求时,比如:整个数据要准确无误的传递给对方,这往往对于一些要求可靠的应用,比如HTTP,HTTPS,FTP等传输文件的协议,POP,SMTP等邮件的传输协议。下载东西

TCP如何保证可靠性
以下是TCP提供可靠性的方式:
(1)应用数据被分割成TCP认为的最合适发送的数据块;
(2)当TCP发出一个报文段后,就启动一个定时器,用来等待目的端确认收到这个报文段;
若没能及时收到这个确认,TCP发送端将重新发送这个报文段(超时重传);
(3)TCP收到一个发自TCP连接的另一端的数据后就将发送一个确认,
不过这个确认不是立即就发送,而是要推迟几分之一秒后才发送;
(4)TCP将保持它的首部和数据的检验和;(这是一个端到端的检验和,为了检验数据在传输过程中发生的错误;
若检测到段的检验和有差错,TCP将丢弃和不确认收到此报文段并希望发端可以进行超时重传)
(5)由于TCP报文段是作为IP数据报来传输的,又因为IP数据报的到达可能会失序,所以TCP报文段的到达也可能会失序;
因此,有必要的话TCP会对收到的数据进行重新排序后交给应用层;
(6)因为TCP报文段是作为IP数据报来传输的,并且IP数据报可能会发生重复,所以TCP的接收端必须丢弃掉重复的数据;
(7)TCP提供流量控制;(因为TCP连接的每一方都有固定大小的缓冲空间,
TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据,这一限制可以防止较快主机致使较慢主机的缓冲区溢出)

拥塞控制的作用:
发生原理:资源的需求>可用资源
作用:避免过多的数据注入到网络中,导致网络中的路由器或链路过载。
对比流量控制:拥塞控制是一个全局的过程,涉及到所有的主机,路由器,以及所有降低和网络相关的所有因素。
流量控制往往指点对点通信量的控制,是端对端的问题。

TCP控制拥塞实现的方法
1.慢开始
2.拥塞避免
3.快重传
4.快恢复
慢算法和拥塞避免:;cwnd(拥塞窗口)先成倍增加,当cwnd大于等于慢开始门限时,缓慢增加(每次增加1),
当网络出现拥塞时,慢开始门限减小为出现拥塞时cwnd的一半,cwnd设置为1开始执行慢开始算法;
快重传和快回复:当连续接收到三个重复确认时,慢开始门限减少为一半,然后将cwnd设置为慢开始门限,然后开始执行拥塞避免算法。

UDP 文件传输协议
对当前网络通讯质量要求不高的时候,要求网络通讯速度尽量的快,这时就使用UDP。日常生活中常见使用UDP协议: 1.QQ语音 2.QQ视频

常见的HTTP状态码
1XX:通知。1XX系列响应代码仅在与HTTP服务器沟通时使用。
2XX: 成功。2XX系列响应代码表明操作成功了。200(“OK”)
3XX 重定向。3XX系列响应代码表明:客户端需要做些额外工作才能得到所需要的资源。
4XX:客户端错误。这些响应代码表明客户端出现错误。不是认证信息有问题,就是表示格式或HTTP库本身有问题。客户端需要自行改正。404(“Not Found”) 和410(“Gone”)当客户端所请求的URI不对应于任何资源时,发送此响应代码
5XX 服务端错误。这些响应代码表明服务器端出现错误。500(“Internal Server Error”)这是一个通用的服务器错误响应

OSI参考模型
<1> 应用层:为应用层程序提供服务。HTTP,HTTPS,FTP,POP3、SMTP
<2> 表示层:数据格式转化,数据加密
<3> 会话层:建立、管理、维护会话
<4> 传输层:建立管理、维护端到端的连接 .TCP、UDP
<5> 网络层:IP地址及路由选择. IP,ICMP
<6>数据链路层:提供介质访问和链路管理 ARP
<7> 物理层
TCP/IP五层模型:就是应用层、表示层、会话层合成一个应用层

cookie 和session 的区别
Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。
cookie分发是通过扩展HTTP协议来实现的,服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie
经常被使用的一种技术叫做URL重写,就是把session id直接附加在URL路径的后面
1、cookie数据存放在客户的浏览器上,session数据放在服务器上。
2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,考虑到安全应当使用session。
3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用COOKIE。
4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。

浏览器中输入URL到页面返回的全过程
第一步、浏览器中输入域名www.baidu.com
第二步、域名解析 浏览器会把输入的域名解析成对应的IP
浏览器缓存-本机的host文件-路由器缓存-本地DNS服务器-顶级域名服务器
第三步、浏览器与目标服务器建立TCP连接 。TCP3次握手连接
第四步、浏览器通过http协议向目标服务器发送请求 。浏览器向主机发起一个HTTP-GET方法报文请求。请求中包含访问的URL,也就是http://www.baidu.com/ ,KeepAlive,长连接,
还有User-Agent用户浏览器操作系统信息,编码等
第五步、服务器给出响应,将指定文件发送给浏览器 。状态行,响应头,响应实体内容,,表示服务器可以响应请求,返回报文
第六步、TCP释放链接 。 四次挥手
第七步、浏览器显示页面中所有文本。

三次握手是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步标志位。
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;ACK:确认标志

为什么不能用两次握手进行连接?
死锁是可能发生的。比如是A机要连到B机,结果发送的连接信息由于某种原因没有到达B机;于是,A机又发了一次,结果这次B收到了,于是就发信息回来,两机就连接。
传完东西后,断开。结果这时候,原先没有到达的连接信息突然又传到了B机,于是B机发信息给A,然后B机就以为和A连上了,这个时候B机就在等待A传东西过去。最后 两个机器进入无限的等待中。。。。。

TCP的连接的拆除需要发送四个包,因此称为四次挥手
(1) TCP客户端发送一个FIN,用来关闭客户到服务器的数据传送。 FIN:结束标志
(2) 服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。
(3) 服务器关闭客户端的连接,发送一个FIN给客户端。
(4) 客户端发回ACK报文确认,并将确认序号设置为收到序号加1。

为什么连接的时候是三次握手,关闭的时候却是四次握手?
因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。
但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,
所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手

为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态
虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文

get和post的区别
GET把参数包含在URL中,POST通过报文传递参数
get传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制
get安全性非常低,post安全性较高
GET产生一个TCP数据包;POST产生两个TCP数据包 GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200,
对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data

加密
MD5是一种单向加密,它的加密不可逆,它将任意长度的字符串,经过算法计算后生成固定长度的数据,一般为16位表示。登陆
非对称加密:它使用了一对密钥,公钥和私钥私钥只能由一方安全保管,不能外泄,而公钥则可以发给任何请求它的人。非对称加密使用这对密钥中的一个进行加密,而解密则需要另一个密钥。RSA
比如,你向银行请求公钥,银行将公钥发给你,你使用公钥对消息加密,那么只有私钥的持有人–银行才能对你的消息解密。与对称加密不同的是,银行不需要将私钥通过网络发送出去,因此安全性大大提高。
对称加密:是最快速、最简单的一种加密方式,加密与解密用的是同样的密钥。AES,DES
(1) 对称加密加密与解密使用的是同样的密钥,所以速度快,但由于需要将密钥在网络传输,所以安全性不高。
(2) 非对称加密使用了一对密钥,公钥与私钥,所以安全性高,但加密与解密速度慢。
(3) 解决的办法是将对称加密的密钥使用非对称加密的公钥进行加密,然后发送出去,接收方使用私钥进行解密得到对称加密的密钥,然后双方可以使用对称加密来进行沟通。

海量数据处理之–哈希分治
海量的数据通过hash 映射的方法分割成相应的小块数据
hash_map 统计。对每个小数据块,采用trie 树/hash_map 等统计
堆/归并排序

进程和线程
进程是操作系统分配和管理资源的基本单位,每一个进程都有一个自己的地址空间。管道 信号量 消息队列 共享内存 套接字
线程是进程的一部分,共享进程的地址空间,是CPU 调度的一个基本单位。 锁机制:包括互斥锁、条件变量、读写 线程间的通信目的主要是用于线程同步

线程的状态
1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其它线程调用了该对象的start()方法。该状态的线程位于可执行线程池中,变得可执行,等待获取CPU的使用权。
3、执行状态(Running):就绪状态的线程获取了CPU。执行程序代码。
4、堵塞状态(Blocked):堵塞态是线程由于某种原因放弃CPU使用权。临时停止执行。直到线程进入就绪状态,才有机会转到执行状态。
5、死亡状态(Dead):线程运行状完了或者因异常退出了run()方法,该线程结束生命周期。

线程中断
若是我们调用线程的中断方法,当程序即将进入或是已经进入阻塞调用的时候,那么这个中断信号应该由InterruptedException捕获并进行重置;
当run()方法程序段中不会出现阻塞操作的时候,这时候中断并不会抛出异常,我们需要通过interrupted()方法进行中断检查和中断标志的重置。
另外,知道IO操作和synchronized上的阻塞不可中断也是必要的。

多线程
Runnable接口和继承Thread类和Callable接口区别
1、java的单继承,当继承了Thread类,则不能继承其他类,而实现Runnable接口可以
2、Runnable线程类是实例化一个对象o之后,通过多次new Thread(o).start();启动多个线程,而这几个线程属于一个对象,对象的成员变量是同一个。
Thread每个线程启动都对应多个对象,他们的成员变量是独立的
3、Callable支持泛型,call() 方法支持抛出异常,有返回值

多线程同步
1、同步方法,同步代码块 2、volatile 3、可重入锁 ReenreantLock 4、阻塞队列

ThreadLocal:为每个使用该变量的线程分配一个独立的副本。每个线程都可以独立的改变自己的副本,而不影响其他线程的副本,从而隔离了多个线程访问数据的冲突。
概括的说,对于多线程资源共享的问题,线程同步机制采取了时间换空间的方式,访问串行化,对象共享化;而ThreadLocal采取了空间换时间的方式,访问并行化,对象独享化。
如何维护副本:在ThreadLocal类中有一个Map,Map中的键为线程对象,值为对应线程的变量副本。

线程池:ThreadPoolExecutor 用于创建线程池的
1、int corePoolSize:该线程池中核心线程数最大值 。线程池新建线程的时候,如果当前线程总数小于corePoolSize,则新建的是核心线程。
2、int maxi1mumPoolSize: 该线程池中线程总数最大值 线程总数 = 核心线程数 + 非核心线程数。
3、keepAliveTime:线程的最大生命周期。一个非核心线程,如果不干活(闲置状态)的时长超过这个参数所设定的时长,就会被销毁掉,
4、TimeUnit unit:keepAliveTime的单位
5、BlockingQueue workQueue:该线程池中的任务队列:维护着等待执行的Runnable对象。当线程池中的线程都处于运行状态,而此时任务数量继续增加,则需要一个容器来容纳这些任务,这个任务队列是一个阻塞式的单端队列。
SynchronousQueue 这个队列接收到任务的时候,会直接提交给线程处理,而不保留它。使用这个类型队列的时候,maximumPoolSize一般指定成Integer.MAX_VALUE,即无限大防止发生错误
LinkedBlockingQueue:这个队列接收到任务的时候,如果当前线程数小于核心线程数,则新建线程(核心线程)处理任务;如果当前线程数等于核心线程数,则进入队列等待。由于这个队列没有最大值限制,
即所有超过核心线程数的任务都将被添加到队列中,这也就导致了maximumPoolSize的设定失效,因为总线程数永远不会超过corePoolSize
ArrayBlockingQueue:可以限定队列的长度,接收到任务的时候,如果没有达到corePoolSize的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,
则新建线程(非核心线程)执行任务,又如果总线程数到了maximumPoolSize,并且队列也满了,则发生错误
DelayQueue:队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务
6、threadFactory:定义如何启动一个线程,可以设置线程的名称,并且可以确定是否是后台线程等。一般用不上。
7、handler:拒绝任务处理器。由于超出线程数量和队列容量而对继续增加的任务进行处理的程序
AbortPolicy策略,为java线程池默认的阻塞策略,不执行此任务,而且直接抛出一个运行时异常,
CallerRunsPolicy策略,只要线程池未关闭,该策略直接在调用者线程中运行当前被丢弃的任务。显然这样不会真的丢弃任务,但是,调用者线程性能可能急剧下降。
DiscardOldestPolicy策略,从队列里面抛弃head的一个任务,并再次execute 此task
DiscardPolicy策略,直接抛弃,任务不执行,空方法

阻塞队列
ArrayBlockingQueue是一个用数组实现的有界阻塞队列。此队列按照先进先出(FIFO)的原则对元素进行排序。
默认情况下不保证访问者公平的访问队列,所谓公平访问队列是指阻塞的所有生产者线程或消费者线程,
当队列可用时,可以按照阻塞的先后顺序访问队列,即先阻塞的生产者线程,可以先往队列里插入元素,先阻塞的消费者线程,可以先从队列里获取元素。通常情况下为了保证公平性会降低吞吐量
LinkedBlockingQueue是一个用链表实现的有界阻塞队列。此队列的默认和最大长度为Integer.MAX_VALUE。此队列按照先进先出的原则对元素进行排序。
DelayQueue是一个支持延时获取元素的无界阻塞队列。队列使用PriorityQueue来实现。
队列中的元素必须实现Delayed接口,在创建元素时可以指定多久才能从队列中获取当前元素。只有在延迟期满时才能从队列中提取元素。
SynchronousQueue是一个不存储元素的阻塞队列。每一个put操作必须等待一个take操作,否则不能继续添加元素。
SynchronousQueue可以看成是一个传球手,负责把生产者线程处理的数据直接传递给消费者线程。队列本身并不存储任何元素,非常适合于传递性场景
PriorityBlockingQueue是一个支持优先级的无界队列。默认情况下元素采取自然顺序排列,也可以通过比较器comparator来指定元素的排序规则。元素按照升序排列。

数组和链表
数组静态分配内存,连续,查询速度快 O(1) 增删慢O(n) 大小固定 不能动态扩展
链表动态分配内存,不连续,查询速度慢 O(n) 增删快O(1) 大小不固定 能动态扩展

二叉查找树,AVL树,B树,B+树,红黑树
二叉查找树就是左结点小于根节点,右结点大于根节点的一种排序树,也叫二叉搜索树。二叉查找树比普通树查找更快,查找、插入、删除的时间复杂度为O(logN)。
但是二叉查找树有一种极端的情况,就是会变成一种线性链表似的结构。此时时间复杂度就变味了O(N),为了解决这种情况,出现了二叉平衡树
平衡二叉树全称平衡二叉搜索树,也叫AVL树。左子树和右子树的高度差不得超过1。查找、插入、删除的时间复杂度都为O(logN),
但是由于要维持自身的平衡,所以进行插入和删除结点操作的时候,需要对结点进行频繁的旋转
AVL树每一个节点只能存放一个元素,并且每个节点只有两个子节点。当进行查找时,就需要多次磁盘IO,
(数据是存放在磁盘中的,每次查询是将磁盘中的一页数据加入内存,树的每一层节点存放在一页中,不同层数据存放在不同页。)
这样如果需要多层查询就需要多次磁盘IO。为了解决AVL树的这个问题,就出现了B树
B树也叫平衡树,是一种多路平衡树。每个中间节点都包含k-1个元素和k个孩子,每一个叶子节点都包含k-1个元素,所有的叶子结点都位于同一层,每个节点中的元素从小到大排列
B+树 每个中间节点包含有k个元素 每个非叶子结点存放的元素只用于索引作用,所有数据保存在叶子结点。
因为非叶子结点中存放的元素不存放数据,所以每一层可以容纳更多元素,也就是磁盘中的每一页可以存放更多元素。这样在查找时,磁盘IO的次数也会减少
B+树的查找稳定,因为所有的数据都在叶子结点。每个叶子结点也通过指针指向构成了一种链表结构,所以遍历数据也会简单很多
红黑树是一种自平衡的二叉查找树,它的节点的颜色为红色和黑色。它不严格控制左、右子树高度或节点数之差小于等于1
1.节点是红色或黑色。
2.根节点是黑色。
3.每个叶子节点都是黑色的空节点(NIL节点)。
4 每个红色节点的两个子节点都是黑色。也就是说从每个叶子到根的所有路径上不能有两个连续的红色节点)。
5.从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

单例模式:保证类对象在运行期,只存在一个实例 (特定场合下的需要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务)。
好处:一则,解决多线程并发访问的问题。二则节约系统内存,提交系统运行的效率,提高系统性能。

public class Singleton4 {
// 私有构造
private Singleton4() {}

private volatile static Singleton4 single = null;

// 双重检查
public static Singleton4 getInstance() {
    if (single == null) {
        synchronized (Singleton4.class) {
            if (single == null) {
                single = new Singleton4();
            }
        }
    }
    return single;
}

}
工厂模式: 用于创建多个实例,确保每个实例都是不同的对象
工厂模式是为了解耦:把对象的创建和使用的过程分开。就是Class A 想调用 Class B ,那么A只是调用B的方法,而至于B的实例化,就交给工厂类。工厂模式可以降低代码重复
class Child{
int age = 10;
int weight = 30;
public static Child newChild(int age, int weight) {
Child child = new Child();
child.weight = weight;
child.age = age;
return child;
}
}
原型模式(Prototype)模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象
public class Prototype implements Cloneable {

public Object clone() throws CloneNotSupportedException {
	Prototype proto = (Prototype) super.clone();
	return proto;
}

}
装饰模式 指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。IO 流是典型的装饰模式
装饰设计模式: Decorator模式(别名Wrapper):动态将职责附加到对象上,
若要扩展功能,装饰者提供了比继承更具弹性的代替方案
当要对已有的对象进行功能增强时,可以定义类,将已有对象传入,
基于已有对象的功能,并提供加强功能。那么自定义的该类就称为装饰类。
装饰类通常会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能,提供更强的功能
装饰模式
class Test {
public static void main(String[] args) {
Japan japan=new Japan();

			NiceJapan niceJapan=new NiceJapan(japan);
			niceJapan.speak();	
		}
	}

	class Japan{
		void speak() {
			System.out.println("你好");
		}
	}
	
	class NiceJapan  {
		NiceJapan(Japan japan){
			this.japan=japan;
		}
		Japan japan;
		
		void speak() {
			System.out.println("点点头");
			japan.speak(); 
			System.out.println("弯弯腰");
		}
	}

策略模式(Strategy) 指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。思想(针对一组算法,将每一个算法封装到具有共同接口的独立的类中,使得它们可以互相替换。)
比如每个人都要“交个人所得税”,但是“在美国交个人所得税”和“在中国交个人所得税”就有不同的算税方法。
当系统能在几种算法中快速地切换,或系统中有一些类,它们仅行为不同时,或系统中存在多重条件选择语句时,可以考虑采用策略模式

代理模式(Proxy) 代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
作用:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
1、静态代理
静态代理:由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。
2.动态代理
代理类在程序运行时创建的代理方式被成为动态代理。
实现:在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。

责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。
发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。
Struts2 的拦截器,jsp servlet 的 Filter就是使用了责任链模式
优点: 1、降低耦合度。它将请求的发送者和接收者解耦。2、增强给对象指派职责的灵活性

观察者模式(有时又被称为模型(Model)-视图(View)模式、源-收听者(Listener)模式或从属者模式) 在对象之间定义了一对多的依赖,
这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新。比如有一个微信公众号服务,不定时发布一些消息,关注公众号就可以收到推送消息,取消关注就收不到推送消息。

冒泡排序
for(int i=0;i<x.length-1;i++ ) {
for(int j=0;j<x.length-1-i;j++) {
if(x[j]>x[j+1]) {
int temp=x[j];
x[j]=x[j+1];
x[j+1]=temp;
}
}
}
冒泡排序总的平均时间复杂度为O(n的平方) 是一种稳定排序算法

插入排序
public static int[] insertSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
for (int j = i; j > 0; j–) {
if (arr[j] < arr[j - 1]) {
// TODO:
int temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
} else {
// 接下来是无用功
break;
}
}
}
return arr;
}
适用于少量数据的排序,时间复杂度为O(n^2)。是稳定的排序方法

快速排序
static void quickSort(int[] x, int low, int high) {
if (low < high) {
int m = partSourt(x, low, high);

		quickSort(x, low, m - 1);

		quickSort(x, m + 1, high);
	}

}

static int partSourt(int[] x, int low, int high) {

	int mid = x[low]; // 中间的那个数

	while (low < high) {
		while (low < high && x[high] >= mid) {
			high--;
		}

		x[low] = x[high];

		while (low < high && x[low] <= mid) {
			low++;
		}

		x[high] = x[low];
	}

	// 把中间值放到它该放的地方
	x[low] = mid;

	return low;

}

最快时间复杂度O(n的平方),平均时间复杂度O(nlogn),不稳定,占用空间

堆排序:堆是一棵顺序存储的完全二叉树。其中每个结点的关键字都不大于其孩子结点的关键字,这样的堆称为小根堆。其中每个结点的关键字都不小于其孩子结点的关键字,这样的堆称为大根堆。
(1)根据初始数组去构造初始堆(构建一个完全二叉树,保证所有的父结点都比它的孩子结点数值大)。

(2)每次交换第一个和最后一个元素,输出最后一个元素(最大值),然后把剩下元素重新调整为大根堆。
当输出完最后一个元素后,这个数组已经是按照从小到大的顺序排列了。
平均、最好、最坏时间复杂度:O(nlogn),不稳定,适用于大数据量的排序

归并排序 :该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序
平均时间复杂度O(nlogn),稳定,占用空间

二分查找
public static int binarySearch(Integer[] srcArray, int des) {
//定义初始最小、最大索引
int low = 0;
int high = srcArray.length - 1;
//确保不会出现重复查找,越界
while (low <= high) {
//计算出中间索引值
int middle = (high + low)>>>1 ;//防止溢出
if (des == srcArray[middle]) {
return middle;
//判断下限
} else if (des < srcArray[middle]) {
high = middle - 1;
//判断上限
} else {
low = middle + 1;
}
}
//若没有,则返回-1
return -1;
}

IO复用,AIO,BIO,NIO,同步,异步,阻塞和非阻塞
BIO:同步阻塞:排队等买小龙虾
NIO:同步非阻塞:预定了小龙虾后去干其他事情,期间自己回来看小龙虾做好没
AIO:异步非阻塞:预定了小龙虾后去干其实事情,小龙虾做好了通知我
同步:执行一个操作之后,进程触发IO操作并等待(也就是我们说的阻塞)或者轮询的去查看IO操作(也就是我们说的非阻塞)是否完成,等待结果,然后才继续执行后续的操作。
异步:执行一个操作后,可以去执行其他的操作,然后等待通知再回来执行刚才没执行完的操作。
阻塞:进程给CPU传达一个任务之后,一直等待CPU处理完成,然后才执行后面的操作。
非阻塞:进程给CPU传达任务后,继续处理后续的操作,隔断时间再来询问之前的操作是否完成。这样的过程其实也叫轮询。
IO的方式通常分为几种,同步阻塞的BIO、同步非阻塞的NIO、异步非阻塞的AIO。
同步阻塞IO:在此种方式下,用户进程在发起一个IO操作以后,必须等待IO操作的完成,只有当真正完成了IO操作以后,用户进程才能运行。JAVA传统的IO模型属于此种方式!
同步非阻塞IO:在此种方式下,用户进程发起一个IO操作以后边可返回做其它事情,但是用户进程需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问,
从而引入不必要的CPU资源浪费。其中目前JAVA的NIO就属于同步非阻塞IO。

Linux
查看当前进程: ps
执行退出: exit
查看当前路径: pwd
它用于切换当前目录:cd
查看文件与目录:ls
该命令常用于分析一行的信息,若当中有我们所需要的信息,就将该行显示出来:grep
基于查找的功能非常强大的命令:find
复制文件:cp
该命令用于查看文本文件的内容:cat
用于改变文件所属用户组:chgrp
用于改变文件的所有者:chown
用于改变文件的权限:chmod
哪个命令专门用来查看后台任务? job -l
终止进程用什么命令? 带什么参数? kill -9 pid
使用什么命令查看网络是否连通? netstat
使用什么命令查看 ip 地址及接口信息? ifconfig
查看各类环境变量用什么命令?查看所有 env

秒杀系统设计
消息队列,redis ,服务器集群
采用redis的分布式乐观锁,解决高并发下的超买超卖问题.
使用countDownLatch作为计数器,将数据四线程写入数据库

2.1 前端方案
静态资源缓存:将活动页面上的所有可以静态的元素全部静态化,尽量减少动态元素;编程HTML文件。通过CDN缓存静态资源,来抗峰值。
禁止重复提交:用户提交之后按钮置灰,禁止重复提交
用户限流:在某一时间段内只允许用户提交一次请求,比如可以采取IP限流
2.2 中间代理层
可利用负载均衡(例如反响代理Nginx等)使用多个服务器并发处理请求,减小服务器压力。
2.3 后端方案 (控制层)

对秒杀的商品利用redis缓存,对商品采用redisTemplate创建list集合,对秒杀成功的用户用redisTemplate创建set存储。
使用rabitMQ进行流量消峰处理,连接订单系统处理(创建交换机和队列并连接),通过rabitTemplate写入消息队列之后,订单系统通过注解配置@rabitListener交换机和队列消费数据写入数据库。

采用消息队列缓存请求:将大流量请求写到消息队列缓存,利用服务器根据自己的处理能力主动到消息缓存队列中抓取任务处理请求,
数据库层订阅消息减库存,减库存成功的请求返回秒杀成功,失败的返回秒杀结束。
利用缓存应对写请求:缓存也是可以应对写请求的,可把数据库中的库存数据转移到Redis缓存中,所有减库存操作都在Redis中进行,然后再通过后台进程把Redis中的用户秒杀请求同步到数据库中
2.4 数据库层
数据库层是最脆弱的一层,一般在应用设计时在上游就需要把请求拦截掉,数据库层只承担“能力范围内”的访问请求。所以,上面通过在服务层引入队列和缓存,让最底层的数据库高枕无忧。

Jstat 这是一个比较实用的一个命令,可以观察到classloader,compiler,gc相关信息。可以时时监控资源和性能
Jmap 得到运行java程序的内存分配的详细情况。例如实例个数,大小等
压力测试 使用JMeter 压测工具
下载、安装、进入C:/JMeter/bin下面的jmeter.bat批处理文件来启动JMeter的可视化界面,
进入测试计划添加线程组: 设置线程数,循环次数,添加HTTP默认请求,服务器名称,IP,以及自己设定的携带参数
添加监听器,存放测试结果:聚合报告,可以表格查询、图形结果、树结果
点击运行-》启动。

面向对象设计七大原则
1. 单一职责原则(Single Responsibility Principle)
每一个类应该专注于做一件事情。
2. 里氏替换原则(Liskov Substitution Principle)
超类存在的地方,子类是可以替换的。
3. 依赖倒置原则(Dependence Inversion Principle)
实现尽量依赖抽象,不依赖具体实现。
4. 接口隔离原则(Interface Segregation Principle)
应当为客户端提供尽可能小的单独的接口,而不是提供大的总的接口。
5. 迪米特法则(Law Of Demeter)
又叫最少知识原则,一个软件实体应当尽可能少的与其他实体发生相互作用。
6. 开闭原则(Open Close Principle)
面向扩展开放,面向修改关闭。
7. 组合/聚合复用原则(Composite/Aggregate Reuse Principle CARP)1
尽量使用合成/聚合达到复用,尽量少用继承。原则: 一个类中有另一个类的对象。

分布式、集群、负载均衡
负载均衡:客户端的流量首先会到达负载均衡服务器,由负载均衡服务器通过一定的调度算法将流量分发到不同的应用服务器上面,
同时负载均衡服务器也会对应用服务器做周期性的健康检查,当发现故障节点时便动态的将节点从应用服务器集群中剔除,以此来保证应用的高可用。
负载均衡又分为四层负载均衡和七层负载均衡。四层负载均衡工作在OSI模型的传输层,主要工作是转发,它在接收到客户端的流量以后通过修改数据包的地址信息将流量转发到应用服务器
七层负载均衡工作在OSI模型的应用层,因为它需要解析应用层流量,所以七层负载均衡在接到客户端的流量以后,还需要一个完整的TCP/IP协议栈。
七层负载均衡会与客户端建立一条完整的连接并将应用层的请求流量解析出来,再按照调度算法选择一个应用服务器,并与应用服务器建立另外一条连接将请求发送过去,
因此七层负载均衡的主要工作就是代理。

Nginx使用步骤:配置Nginx.conf 写入upstream配置服务器池,配置server监听80端口。配置nginx负载均衡策略:在upstream中加入相应的配置语句即可
session不同步问题:比如在一台服务器上登陆以后,再次刷新请求被送到另一台服务器,此时用户登录的session为null,要求重新登陆。
解决:可以把session保存到redis中,全局保存一份,所有的服务器都去访问redis就可以了
利用Nginx静态资源缓存(css,js文件)减低Tomcat压力,在Nginx.conf中配置。利用Gzip压缩算法 Nginx打包压缩静态资源降低带宽
流量防刷与反爬虫:就是在一段时间内限制用户的请求次数。在redis中维护一个设置过期时间的key,用户每访问一次加1。用SprigMVC拦截器实现

负载均衡算法:加权轮询(Weight Round Robin)法:不同的后台服务器可能机器的配置和当前系统的负载并不相同,因此它们的抗压能力也不一样。跟配置高、负载低的机器分配更高的权重,
使其能处理更多的请求,而配置低、负载高的机器,则给其分配较低的权重,降低其系统负载,加权轮询很好的处理了这一问题,并将请求按照顺序且根据权重分配给后端。

加权随机(Weight Random)法:加权随机法跟加权轮询法类似,根据后台服务器不同的配置和负载情况,配置不同的权重。不同的是,它是按照权重来随机选取服务器的,而非顺序。
源地址哈希法:
源地址哈希法的思想是根据服务消费者请求客户端的IP地址,通过哈希函数计算得到一个哈希值,
将此哈希值和服务器列表的大小进行取模运算,得到的结果便是要访问的服务器地址的序号。

负载均衡的实现(DNS > 数据链路层 > IP层 > Http层)

1.DNS域名解析负载均衡(延迟)
2.数据链路层负载均衡
是指在通信协议的数据链路层修改mac地址进行负载均衡,不修改IP地址。
3. IP负载均衡(SNAT)
即在网络层通过修改请求目标地址进行负载均衡。
4.HTTP重定向负载均衡(少见)
5.反向代理负载均衡(nginx)
也叫应用层负载均衡(Http协议)

反向代理负载均衡(nginx) ,使用tomcat+nginx搭建服务器集群
反向代理是代理服务器的一种。客户端的请求会先到达代理服务器,代理服务器根据算法找到一台合适的后台服务器,然后再将请求资源返回给客户端,
客户端只会得知反向代理的IP地址,而不知道在代理服务器后面的服务器簇的存在。

集群:同一个业务,部署在多个服务器上

分布式:一个业务分拆成多个子业务,或者本身就是不同的业务,部署在不同的服务器上

分布式系统的CAP理论:理论首先把分布式系统中的三个特性进行了如下归纳:
一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
分区容忍性(P):以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。

BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的简写,
BASE是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的结论,
是基于CAP定理逐步演化而来的,其核心思想是即使无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。

基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性——但请注意,这绝不等价于系统不可用。如响应时间上的损失
最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。
因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性

分布式事务:指事务的每个操作步骤都位于不同的节点上,需要保证事务的 ACID 特性。减少库存同时更新订单状态。库存和订单不在不同一个数据库,因此涉及分布式事务。
解决方案:消息中间件也可称作消息队列 (MQ),它本质上是一个暂存转发消息的一个中间件。在分布式应用当中,我们可以把一个业务操作转换成一个消息,
比如支付宝的余额转如余额宝操作,支付宝系统执行减少余额操作之后向消息系统发一个消息,余额宝系统订阅这条消息然后进行增加账户金额操作。

比如:一个应用有手机 APP 端和 Web 端,如果在两个客户端同时进行一项操作时,那么就会导致这项操作重复进行。
分布式的CAP理论告诉我们“任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。
三种实现方式:基于数据库实现分布式锁; 基于缓存(Redis等)实现分布式锁; 基于Zookeeper实现分布式锁;

基于数据库的实现方式的核心思想是:在数据库中创建一个表,表中包含方法名等字段,并在方法名字段上创建唯一索引,
想要执行某个方法,就使用这个方法名向表中插入数据,成功插入则获取锁,执行完成后删除对应的行数据释放锁。

基于redis的实现思想:
(1)获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。
(2)获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。
(3)释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。

redis支持的数据类型
String字符串:格式: set key value。string类型是二进制安全的
Hash(哈希)格式: hmset name key1 value1 key2 value2
List(列表)Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)格式: lpush name value
Set(集合)格式: sadd name value。Redis的Set是string类型的无序集合。
zset(sorted set:有序集合)格式: zadd name score value。Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。

为什么说redis能够快速执行
(1) 绝大部分请求是纯粹的内存操作(非常快速)
(2) 采用单线程,避免了不必要的上下文切换和竞争条件
(3) 非阻塞IO - IO多路复用

redis 最适合的场景
如果简单地比较Redis与Memcached的区别,大多数都会得到以下观点:
1 、Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
2 、Redis支持数据的备份,即master-slave模式的数据备份。
3 、Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。
(1)、会话缓存(Session Cache)
(2)、队列 Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得Redis能作为一个很好的消息队列平台来使用。
(3)、排行榜/计数器、 Redis在内存中对数字进行递增或递减的操作实现的非常好。Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得Redis能作为一个很好的消息队列平台来使用。
(4)、发布/订阅

redis系列之数据库与缓存数据一致性解决方案
应该是先删除缓存,然后在更新数据库,如果删除缓存失败,那就不要更新数据库,如果说删除缓存成功,而更新数据库失败,
那查询的时候只是从数据库里查了旧的数据而已,这样就能保持数据库与缓存的一致性。

缓存失效策略(FIFO 、LRU、LFU三种算法的区别)
当缓存需要被清理时(比如空间占用已经接近临界值了),需要使用某种淘汰算法来决定清理掉哪些数据。常用的淘汰算法有下面几种:
FIFO:First In First Out,先进先出。判断被存储的时间,离目前最远的数据优先被淘汰。
LRU:Least Recently Used,最近最少使用。判断最近被使用的时间,目前最远的数据优先被淘汰。
LFU:Least Frequently Used,最不经常使用。在一段时间内,数据被使用次数最少的,优先被淘汰。

不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
什么是Redis持久化?Redis有哪几种持久化方式?优缺点是什么?
持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。Redis 提供了两种持久化方式:RDB(默认) 和AOF
RDB就是将数据库中所有的数据保存到硬盘当中,以RDB文件的形式存在,这是一个非常耗时、耗资源的操作,因此服务器需要隔一段时间才创建新的RDB文件。这使得服务器意外停机时会丢失大量数据
AOF将每个修改了数据库的命令写入到AOF文件末尾,再启动服务器的时候重新执行这些命令,就可以还原数据库中的数据。
因为写入缓冲区的存在,AOF的安全性在于缓冲区的命令何时能被写入到磁盘里,通过设置appendfsync配置选项解决。AOF冗余命令会很多。通过AOF的重写

redis主从复制实现
master服务器会开启一个后台进程用于将redis中的数据生成一个rdb文件,与此同时,服务器会缓存所有接收到的来自客户端的写命令(包含增、删、改),当后台保存进程
处理完毕后,会将该rdb文件传递给slave服务器,而slave服务器会将rdb文件保存在磁盘并通过读取该文件将数据加载到内存,在此之后master服务器会将在此期间缓存的
命令通过redis传输协议发送给slave服务器,然后slave服务器将这些命令依次作用于自己本地的数据集上最终达到数据的一致性。

Redis 的复制功能允许用户根据一个 Redis 服务器来创建任意多个该服务器的复制品,其中被复制的服务器为主服务器(master),而通过复制创建出来的服务器复制品则为从服务器(slave)。
只要主从服务器之间的网络连接正常,主从服务器两者会具有相同的数据,主服务器就会一直将发生在自己身上的数据更新同步 给从服务器,从而一直保证主从服务器的数据相同。
降低 master 读压力在转交从库

Redis sentinel(哨兵) 是一个分布式系统中监控 redis 主从服务器,并在主服务器下线时自动进行故障转移

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值