努力了那么多年,回头一望,几乎全是漫长的挫折和煎熬。对于大多数人的一生来说,顺风顺水只是偶尔,挫折、不堪、焦虑和迷茫才是主旋律。我们登上并非我们所选择的舞台,演出并非我们所选择的剧本。继续加油吧!
目录
1.对网络协议熟悉哪些?
2.Http状态码?
3.算法题:反转链表
4.合并有序链表
5.算法题:最大子数组和
6.哈希碰撞的解决方法?
7.了解的测试工作有哪些方面?
8.压测相关工具?
9.说一下接口测试?
10.http抓包工具?
11.接口测试中的mock?
12.写单元测试如何判断代码覆盖度?
13.多线程测试?
14.多核是针对线程还是进程?
15.TCP三次握手发出的SYN之后没收到ACK如何处理?
16.四次挥手的timewait?
17.如何测试请求的幂等性?
18.Python里的GIL?
19.算法题:最长无重复子数组
20.无重复字符串的最长字串
21.算法题:链表中倒数最后k个结点
22.算法题:括号生成
常见的网络协议有:TCP/IP协议、UDP协议、HTTP协议、FTP协议、Telnet协议、SMTP协议、NFS协议等。
TCP/IP协议由四个层次组成:
应用层:应用层是TCP/IP协议的第一层,是直接为应用进程提供服务的。
(1)对不同种类的应用程序它们会根据自己的需要来使用应用层的不同协议,邮件传输应用使用了SMTP协议、万维网应用使用了HTTP协议、远程登录服务应用使用了有TELNET协议。
(2)应用层还能加密、解密、格式化数据。
(3)应用层可以建立或解除与其他节点的联系,这样可以充分节省网络资源。
运输层:作为TCP/IP协议的第二层,运输层在整个TCP/IP协议中起到了中流砥柱的作用。且在运输层中,TCP和UDP也同样起到了中流砥柱的作用。
网络层:网络层在TCP/IP协议中的位于第三层。在TCP/IP协议中网络层可以进行网络连接的建立和终止以及IP地址的寻找等功能。
网络接口层:在TCP/IP协议中,网络接口层位于第四层。由于网络接口层兼并了物理层和 数据链路层所以,网络接口层既是传输数据的物理媒介,也可以为网络层提供一条准确无误的线路。
HTTP状态码主要分为5大类,如下:
1xx : 消息,这一类型的状态码,服务端收到请求,需要继续处理。但是一般服务器禁止向客户端发送此类状态码;
2xx : 成功,这一类型的状态码,代表请求已成功被服务器接收并处理;
3xx : 重定向,这类状态码代表需要客户端采取进一步的操作才能完成请求;
4xx : 客户端看起来可能发生了错误,妨碍了服务器的处理;
5xx : 服务器错误,这类状态码代表了服务器在处理请求的过程中有错误或者异常状态发生,也有可能是服务器意识到以当前的软硬件资源无法完成对请求的处理。
下面是常见的 HTTP 状态码:
题目链接:反转链表_牛客题霸_牛客网
public class Solution {
public ListNode ReverseList(ListNode head) {
//递归法
if(head == null || head.next == null){
return head ;
}
ListNode ans = ReverseList(head.next) ;
head.next.next = head ;
head.next = null ;
return ans ;
}
}
直接递归合并即可,依次将大的链表合并到小的链表上,如下:
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
//递归法
if(list1 == null){
return list2 ;
}
if(list2 == null){
return list1 ;
}
//若list1的值更小,则递归合并后接到list1上
if(list1.val <= list2.val){
list1.next = Merge(list1.next,list2) ;
return list1 ;
}else{//反之,递归合并接到list2上
list2.next = Merge(list1,list2.next) ;
return list2 ;
}
}
}
题目链接:连续子数组的最大和_牛客题霸_牛客网
思路:累加到当前的值大于当前值,则可以继续累加,否则,从当前开始累加,累加过程找出最大的。
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
//最好保证时间复杂度为O(n),空间复杂度为O(1)
int sum = array[0] ;
int max = array[0] ;
for(int i=1; i array[i]){
sum += array[i] ;
}else{
sum = array[i] ;
}
max = sum > max ? sum : max ;
}
return max ;
}
}
(1)开放定址法,也称为线性探测法,就是从发生冲突的那个位置开始,按照一定的次序从hash表中找到一个空闲的位置,然后把发生冲突的元素存入到这个空闲位置中。ThreadLocal就用到了线性探测法来解决hash冲突的。
(2)链式寻址法,即拉链法,这是一种非常常见的方法,简单理解就是把存在hash冲突的key,以单向链表的方式来存储,比如HashMap就是采用链式寻址法来实现的。
(3)再hash法,就是当通过某个hash函数计算的key存在冲突时,再用另外一个hash函数对这个key做hash,一直运算直到不再产生冲突。这种方式会增加计算时间,性能影响较大。
(4)建立公共溢出区, 就是把hash表分为基本表和溢出表两个部分,凡事存在冲突的元素,一律放入到溢出表中。
分析需求、编写测试用例、测试用例评审、测试用例修改、功能测试与性能测试等。
按是否查看程序内部结构分为:
(1)黑盒测试(black-box testing):只关心输入和输出的结果
(2)白盒测试(white-box testing):去研究里面的源代码和程序结构
黑盒测试分为功能测试和性能测试:
1)功能测试(function testing),是黑盒测试的一方面,它检查实际软件的功能是否符合用户的需求。
包括逻辑功能测试(logic function testing)
界面测试(UI testing)UI=User Interface
易用性测试(usability testing):是指从软件使用的合理性和方便性等角度对软件系统进行检查,来发现软件中不方便用户使用的地方。
兼容性测试(compatibility testing):包括硬件兼容性测试和软件兼容性测试
2)性能测试(performance testing)
软件的性能主要有时间性能和空间性能两种
时间性能:主要指软件的一个具体事务的响应时间(respond time)。
空间性能:主要指软件运行时所消耗的系统资源。
软件性能测试分为:
一般性能测试:指的是让被测系统在正常的软硬件环境下运行,不向其施加任何压力的性能测试。
稳定性测试也叫可靠性测试(reliability testing):是指连续运行被测系统检查系统运行时的稳定程度。
负载测试(load testing):是指让被测系统在其能忍受的压力的极限范围之内连续运行,来测试系统的稳定性。
压力测试(stress testing):是指持续不断的给被测系统增加压力,直到将被测系统压垮为止,用来测试系统所能承受的最大压力。(Validate the system or software can allowed the biggest stress.)
其它测试:回归测试(regression testing)是指对软件的新的版本测试时,重复执行上一个版本测试时的用例。
压力测试(StressTesting),也称为强度测试,通过模拟实际应用的软硬件环境及用户使用过程的系统负荷,长时间或超大负荷地运行测试软件,来测试被测系统的性能、可靠性、稳定性等。压力测试需要确定一个系统的瓶颈或者不能接收的性能点,来获得系统能提供的最大的服务级别。通俗地讲,压力测试是为了发现在什么条件下您的应用程序的性能会变得不可接受。
1.LoadRunner
LoadRunner是一种预测系统行为和性能的负载测试工具,通过模拟实际用户的操作行为进行实时性能监测,来帮助测试人员更快的查找和发现问题。LoadRunner提供了3大主要功能模块:VirtualUser Generator(用于录制性能测试脚本),LoadRunner Controller(用于创建、运行和监控场景),LoadRunner Analysis(用于分析性能测试结果)既可以作为独立的工具完成各自的功能,又可以作为LoadRunner的一部分彼此衔接,与其他模块共同完成软件性能的整体测试。
2.Apache JMeter
JMeter作为一款广为流传的开源压测产品,最初被设计用于Web应用测试,如今JMeter可以用于测试静态和动态资源,例如静态文件、Java 小服务程序、CGI 脚本、Java 对象、数据库、FTP服务器等等,还能对服务器、网络或对象模拟巨大的负载,通过不同压力类别测试它们的强度和分析整体性能。另外,JMeter能够对应用程序做功能测试和回归测试,通过创建带有断言的脚本来验证你的程序返回了你期望的结果。为了最大限度的灵活性,JMeter允许使用正则表达式创建断言。
接口测试就是通过接口的不同情况下的输入和与之对用的输出之间的关系,看看是否满足接口规范所规定的功能性、安全性以及性能方面的要求.
最知名的就是postman,当然还有restclient之类的 。当然也可以用jmeter,jmeter也可以用来做接口性能测试,当然我们也可以通过编写脚本(代码)进行接口测试。
接口测试要遵循一些要点:
fiddler:windows平台最受欢迎抓包工具、免费、易用
charles:Mac平台下最佳抓包工具,易于使用,收费软件,可以一直使用
wireShark:老牌抓包工具,跨平台,功能齐全、强大
Mock测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。
1. 解决依赖问题:当我们测试一个接口或者功能模块的时候,如果这个接口或者功能模块依赖其他接口或其他模块,那么如果所依赖的接口或功能模块未开发完毕,那么我们就可以使用mock模拟被依赖接口,完成目标接口的测试
2. 单元测试:如果某个功能未开发完成,我们又要进行测试用例的代码编写,我们也可以先模拟这个功能进行测试
3. 模拟复杂业务的接口:实际工作中如果我们在测试一个接口功能时,如果这个接口依赖一个非常复杂的接口业务,那么我们完全可以使用mock来模拟这个复杂的业务接口,其实这个和解决接口依赖是一样的原理
4.前后端联调:如果你是一个前端页面开发,现在需要开发一个功能:根据后台返回的状态展示不同的页面,那么你就需要调用后台的接口,但是后台接口还未开发完成,是不是你就停止这部分工作呢?答案是否定的,你完全可以借助mock来模拟后台这个接口返回你想要的数据
代码覆盖(Code coverage)是软件测试中的一种度量,描述程序中源代码被测试的比例和程度,所得比例称为代码覆盖率。简单来理解,就是单元测试中代码执行量与代码总量之间的比率。
最近项目中,每次Build的时候会触发sonar程序去扫描代码,打出测试覆盖率,也就是coverage。如果不到80%以上,就要去补Test case。可是我们在开发中怎么知道写的unit test的coverage是多少呢?这就要靠Idea的工具了。Idea自带一个默认代码覆盖率工具。当然我们也可以配置第三方,比如jacoco。
可以用Jmeter进行模拟多线程测试。
核心数、线程数:目前主流 CPU 都是多核的。增加核心数目就是为了增加线程数,因为操作系统是通过线程来执行任务的,一般情况下它们是 1:1 对应关系,也 就是说四核 CPU 一般拥有四个线程。但 Intel 引入超线程技术后,使核心数与线程数形成 1:2 的关系。
ACK在网络中丢失,那么Server 端该TCP连接的状态为SYN_RECV,并且会根据 TCP的超时重传机制,会等待3秒、6秒、12秒后重新发送SYN+ACK包,以便Client重新发送ACK包。
而Server重发SYN+ACK包的次数,可以通过设置/proc/sys/net/ipv4/tcp_synack_retries修改,默认值为5.
如果重发指定次数之后,仍然未收到 client 的ACK应答,那么一段时间后,Server自动关闭这个连接,并从半连接队列(syns queue)移除。
TIME_WAIT 等于2 倍的 MSL(报⽂最⼤⽣存时间),比较合理的解释是: 网络中可能存在来⾃发送⽅的数据包,当这些发送⽅的数据包被接收⽅处理后又会向对方发送响应,所以⼀来⼀回需要等待 2 倍的时间。
设置timewait的原因:
过多的 TIME-WAIT 状态主要的危害有两种:
接口幂等性:用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生副作用。
支付案例:用户购买商品后支付,支付扣款成功,返回结果的时候网络异常,此时钱已经扣了。如果用户再次点击按钮,进行第二次扣款,返回结果成功,用户查询余额返发现多扣钱了,流水记录也变成了两条,即没有保证接口的幂等性。
使用幂等性可以防止以下问题:
前端重复提交表单: 在填写一些表格时候,用户填写完成提交,很多时候会因网络波动没有及时对用户做出提交成功响应,致使用户认为没有成功提交,然后一直点提交按钮,这时就会发生重复提交表单请求。
用户恶意进行刷单: 例如在实现用户投票这种功能时,如果用户针对一个用户进行重复提交投票,这样会导致接口接收到用户重复提交的投票信息,这样会使投票结果与事实严重不符。
接口超时重复提交: 很多时候 HTTP 客户端工具都默认开启超时重试的机制,尤其是第三方调用接口时候,为了防止网络波动超时等造成的请求失败,都会添加重试机制,导致一个请求提交多次。
消息进行重复消费: 当使用 MQ 消息中间件时候,如果发生消息中间件出现错误未及时提交消费信息,导致发生重复消费。
使用幂等性最大的优势在于使接口保证任何幂等性操作,免去因重试等造成系统产生的未知的问题。
1)要测试删除接口的幂等性,可以通过连续调用删除 -> 查询 -> 删除 -> 再查询四个接口来实现。
2)业务测试应该聚焦业务的幂等性,并设计各种异常场景的测试用例,来验证是否发生重复扣款。
实现幂等一般有3种方案
1.MVCC(多版本并发控制):在数据更新时需要去比较持有数据的版本号,版本号不一致的操作无法成功
2.表去重:构建唯一性索引,保证某一类数据一旦执行完毕,后续同样的请求再也无法成功写入
3.TOKEN机制:为每一次操作生成一个唯一性的凭证,也就是token。
GIL:又叫全局解释器锁,每个线程在执行的过程中都需要先获取GIL,保证同一时刻只有一个线程在运行,目的是解决多线程同时竞争程序中的全局变量而出现的线程安全问题。它并不是python语言的特性,仅仅是由于历史的原因在CPython解释器中难以移除,因为python语言运行环境大部分默认在CPython解释器中。
简单的总结下就是:Python的多线程在多核CPU上,只对于IO密集型计算产生正面效果;而当有至少有一个CPU密集型线程存在,那么多线程效率会由于GIL而大幅下降。
题目链接:最长无重复子数组_牛客题霸_牛客网
思路:借助队列先进先出的规则实现记录最长无重复子数组的长度。
import java.util.*;
public class Solution {
/**
*
* @param arr int整型一维数组 the array
* @return int整型
*/
public int maxLength (int[] arr) {
// write code here
/**
int len = 0 ;
Queue queue = new LinkedList<>() ;
for (int i = 0; i < arr.length; i++) {
if (!queue.contains(arr[i])) {
queue.add(arr[i]) ;
len = queue.size() > len ? queue.size() : len ;
} else {
while (queue.contains(arr[i])) {
queue.poll() ;
if(!queue.contains(arr[i])){
queue.add(arr[i]) ;
break ;
}
}
len = queue.size() > len ? queue.size() : len ;
}
}
return len ;
*/
int max = 0 ;
Queue queue = new LinkedList<>() ;
for(int res : arr){
while(queue.contains(res)){
queue.poll() ;
}
queue.add(res) ;
max = queue.size() > max ? queue.size() : max ;
}
return max ;
}
}
题目链接:无重复字符串的最长字串
无重复字符的最长子串_阅文集团笔试题_牛客网
这题的思路和上题一样,借助队列实现就可以。
import java.util.* ;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNext()) { // 注意 while 处理多个 case
String s = in.next() ;
int len = 0 ;
Queue queue = new LinkedList<>() ;
for (int i = 0; i < s.length(); i++) {
while (queue.contains(s.charAt(i))) {
queue.poll() ;
}
queue.add(s.charAt(i)) ;
len = queue.size() > len ? queue.size() : len ;
}
System.out.println(len) ;
}
}
}
题目链接:链表中倒数最后k个结点_牛客题霸_牛客网
思路:先走链表长度-k步,然后把剩余的输出即可;若k大于链表长度的,则直接返回null
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* public ListNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pHead ListNode类
* @param k int整型
* @return ListNode类
*/
public ListNode FindKthToTail (ListNode pHead, int k) {
// write code here
int len = 0 ;
ListNode node = pHead ;
while(pHead != null){
pHead = pHead.next ;
len ++ ;
}
if(k > len){
return null ;
}
int leave = len - k ;
for(int i=1; i<=leave; i++){
node = node.next ;
}
return node ;
}
}
题目链接:括号生成
思路:递归实现,如果left>0,则可以加左括号,leftclass Solution {
List