(1)面试官介绍部门业务, 自我介绍环节
(2)Java和Go有什么区别?
这里的话因为他们部门是用Go的,所以应该想看看我对Go的了解程度
回答:
第一点,从语法糖的角度上来讲,像JDK8的Java它的语法糖是比较少的,因此整体来看语法比较工整,Golang的语法糖比较多,所以看起来它的代码也会比较简洁。
第二点,从提供的内置集合来看,Java提供的集合会更加丰富一些,像Java的话会提供一个优先级队列这样的数据结构,其实它本质上就是在内部实现了一个堆,然后我们可以通过定义比较器这样的方式来自定义堆,但是像Go的话就没有提供了。
第三点,从垃圾回收的角度上来讲,像Java里面有很多不同的虚拟机实现,因此它的垃圾回收机制也是多种多样的,比如说有串行化垃圾回收器,有并发收集垃圾回收器,对于判断对象是否死亡这样的方法有引用计数法,有可达性分析,然后像Go语言的话我了解到它内部会使用到三色标记法这样的方法,以及在执行垃圾回收的时候是用了类似了Java中CMS垃圾回收器这样的机制
(2)讲讲虚拟内存是什么
答:这边的话我结合硬件的情况说了一下,目前市场上的内存大概都是8GB
为主流的,但是像现在大部分的游戏可能都达到了上百G的规模,那么这样一个上百G的程序,怎么依托于8GB的内存来实现程序的运行呢?这就是虚拟内存起到的作用。
然后讲了底层的实现:它在底层实际上是通过一个页表的结构来实现,这个页表里面存着一个个的页表项,页表项的内容大概是有一个<有效位(指明这个页是否在主存中),页号(其实就是页表的索引),物理块号(实际物理地址)>
,当程序去读取内存的时候,这时候就会去读取页表,如果发现这个页不在内存中,那么就会发起一个系统调用,这个过程就会使得我们的系统从用户态转为系统态
,将这个页从磁盘调度到内存中,所以这样的话,就可以看到其实并不是程序一次性全部被加载到内存的,而是根据程序的需要将内存页读取到主存中,其中有一个著名的定律叫做二八定律,简单来说就是其实在一个程序运行的过程中,在一段时间内只有20%的内容是会被用到的,其余80%的内容都会被存在磁盘中。
最后总结:通过这样的机制,就可以实现将一个大程序装入到一个小内存中
(3)系统态和用户态是什么?为什么要有这种东西?
答:首先讲讲系统态和用户态分别是什么,关于系统态的话,它实际上是我们的操作系统内核程序运行过程中的一种状态,而用户态则是我们用户自己编写的程序运行过程中的一种状态
。
至于为什么需要有这种东西,我们可以假设如果没有这种东西会怎么样?(然后开始扯)
如果没有系统态,那么就是系统中可以运行任意的用户程序,然后这些用户程序执行过程中会需要去读取内存,操作内存,因为没有系统态,那么也就意味着操作内存的权限下放到了我们的用户程序。
这样的话用户程序就可能会产生一些危险的行为,比如说有:第一个,操作了其他程序所属的内存,第二个,访问了其他程序所属的内存,这两种操作都可能导致一个程序运行得好好的,突然就挂掉了,比如另外一个程序覆盖了另外一个程序的变量,导致空指针异常之类的,因此为了避免这种情况,就设定了系统态,通过系统态这个状态,就可以安全地执行一些危险的操作,避免运行过程中崩溃。
(4)系统调用为什么是安全的?
答:这个问题其实没有接触过,然后凭着自己的理解说了。
系统调用本质上就是操作系统内核程序提供的一套接口程序,这是因为用户程序在运行中,其实都是依托于硬件的,硬件会暴露出来一些接口给上位机使用,但是如果直接这样使用的话,那么就会导致一个情况,就是每更换一套硬件,就需要重写编写程序,这肯定是不行的。
所以的话就需要有系统调用,通过系统调用,第一个,用户对诸如内存,磁盘以及外设的操作全部都被封装好了,在这之上做了例如内存保护之类的机制,可以防止程序之间互相访问、修改各自的内存空间。
其次,对于外部设备这一类可能引起竞争的资源,当发起系统调用来使用这些设备的时候,会通过一定的资源保护机制,来保证外部设备的使用正常,比如说当一个程序正在使用打印机的时候,另一个设备不能够使用。
总而言之,系统调用通过提前定义好对硬件的使用规则以及定义了内存保护机制等操作来实现安全性。
(5)零拷贝技术是什么?
答:备注:问这个问题之前,面试官问了我用没用过消息队列,估计想问卡夫卡的底层原理。
回答原文:就是零拷贝
它并不是说机器没有真正发生的任何一次拷贝,而是说没有发生两个空间的这样的一个相互拷贝
。
就是比如说我们路由器接收一个网卡包,那么呢它首先是要从网卡的那个缓冲区里面把我们的数据,把它复制到我们的内核缓存区,而注意到这两个这个复制它其实都是在我们的内核中完成的,所以的话它这个不算就是不算我们那个意义上的拷贝。
然后的话,当我们的用户程序通过socket
的系统调用来读取内核缓冲区中的数据,这时候其实就是将内核缓冲区的数据传入到用户缓冲区中,这个过程其实就是真正意义上的拷贝,零拷贝想要减少的就是这样的拷贝。
那么零拷贝技术就是它通过一个叫做 MMAP 的技术,通过这个技术来减少这一次的拷贝。
具体来说就是我们刚才讲了,就是这个虚拟内存
,虚拟内存
它里面有个页表,通过页表,就可以将同一块物理内存地址同时映射到两张页表上,这样的话,两个程序只要读取自己的页表,尽管他们的逻辑地址是不同的,但是由于页表中存储的物理地址都是相同的,所以这样的话就实现了一种共享内存的效果,通过这种方式,就减少了两个空间之间的相互拷贝,这个过程的优点主要就是,避免了CPU来干预数据复制,减少开销,第二个的话就是可以避免系统调用的开销。
(6)HTTPS连接建立的过程?
HTTPS,它其实在OSI的七层模型里面,在我们的这一个TCP层到我们的HTTP层,对中间加了一层 TLS/SSL 协议。那么它具体是怎么操作?首先它是先建立了一个TCP连接,也就是说经过一个三次握手,三次握手完了之后还要经历一次TLS/SSL握手。
具体的流程是这样子的:比如说我们现在有一个客户端和一个服务端,然后我们这个客户端要先发起一个ClientHello
,这个的话意思就是说我要发起一个HTTPS连接了,然后的话它会携带几个信息,首先第一个是一个客户端携带一个随机数
,还有我要使用的密码套件
,比如说他要使用的是 RSA 这样的技术,还是说DES这样的技术,还有一个是我当前要执行的一个TLS/SSL协议的一个版本号
,比如说1.1这样的版本,然后他就把这个打包成一个包,然后发给这个服务端。
收到之后就会回复一个server reply
,server reply主要是对于这个我们的客户端做一个就是做一个回应,就是说就是说比如说我确认你的协议了,你的TLS/SSL协议是1.1,然后我服务端这边支持就把然后我就可以确认版本号,然后的话密码套件我这也有,所以话我们可以达成这个协议,否则的话就会终止本次的连接,之后还会加上一个这个服务端产生的随机数,然后接着的话就会把这个包返还给客户端。
然后这个客户端收到这个包之后,会做一个公钥的校验和共享密钥的生成
。
首先把它的这一个发送过来这个 CA 证书
进行一个验证,那它的这个 CA 证书里面会包含一个公钥,通过CA的公钥对其中的公钥进行验证通过之后,就会使用这个公钥进行加密
然后会把它本地产生的一个pre master key
这个东西用CA证书中包含的公钥进行加密,然后发送给服务端。然后再发送一个通知,就是说以后的话我们就用这一个对称秘密钥的方式来进行一个通信,用了服务端在收到这个信息之后,自己也会产生一个对称密钥的这一个这个密钥,然后的话后续的话就会一直用这个密钥来进行后续的通信
(7)为什么HTTPS后续的正常通信都要换成对称密钥?
回答:噢,我们讲就是像这个非对称和对称的话,它其实有它是有一定性能差距的,因为我们常说这一个非对称的它有一对公钥和私钥,那么在在这个公钥要解密的这一个过程中,那么加密和解密过程中它其实内这个性能损耗其实是还是比较严重的,而使用对称加密的话,它性能其实是比较好的。
所以的话在我们的 HTTPS 里面,我们通常是就是要在这个 HTTPS 上就是经常的来回传输东西,你的性能很差的话,那么就会影响我们那个用户的体验。如果是对称密钥的话,它的这个性能比较好。
(8)项目为什么要用redis?
首先它这个demo(黑马点评)
的场景它是基于一个秒杀的场景的,比如说我们的这个天猫双 11 或者是什么抢购之类的,那这时候会有一个千万级
或者是到亿级并发
,或者是反正就很非常高的这个并发。
如果说我们单纯走这个数据库的话,那么它其实性能是比较差的。在我做这个 demo 的过程中,其实也是有去测过,就是说我们去完成一次这样一个下单的请求,其实是大概要 500 毫秒左右的。那你想下来就是如果说是千万级并发,这个钱下,每一个线程都 500 毫秒,那可能会造成这个请求的积压,甚至请求的丢失之类的情况。
所以的话引入 Redis 它主要是因为它最适合用来做这一个和数据库做一个缓存,同时它也能提供一些异步的能力,通过这异步能力来大幅度的改善我们数据库的表现,大概的话,这个场景就是非常适合用 Redis 来做的。
(9)项目相关:Redis提升的QPS是针对什么请求?是读请求还是写请求?
像这个请求的话,它我觉得它是一个写的请求,因为我们刚才的假设是在这一个秒杀场景下的,秒杀场景下的话就意味着我们要向服务端提交一些数据,比如说我们这个用户的ID,这个订单号的ID,还有这一个。嗯,还有这个商品的ID,就要把这三个ID,我们把它传到服务器里面去,让服务器做一个备案,那么的话它其实就是一个写的请求。
(10)项目相关:Redis是做缓存还是做实际存储?
真正的存储其实还是在我们的 MySQL 里面的
。那么Redis它其实上只是起到一个中转的作用,就是说我可能我 MySQL 处理不了那么快了,但是我的 Redis 它因为具有一个可以接受高并发,同时有一个持久化的功能。那么呢我可以基于这两个功能,我可以先把一些数据先放到 Redis 里面去,又让他去接收这样高的并发。在后续我们在后台在启动,就是一些比如说像一些守护线程去轮流的读取我们redis中数据,再把它回填到我们的 MySQL 里面去。
(11)项目相关:Redis的消息队列?
答:这里面试官好像不知道Stream这种数据类型,问了我这个数据类型的操作指令是怎么样的。
Stream是在Redis的新版本中提出的新的数据结构,主要是用来提供一个可靠的消息队列,支持了持久化和消息的可靠消费,其中的核心是一个叫做pending-list
的数据结构,这个pending-list
上存放的消息结构体,如果没有被消费完成的话,就会被设置成一个pending
的状态,如果消费者确实消费成功了,那么就会返回一个ack
,然后redis这边就会将其标志成消费成功。指令大概是:
XADD myStream * ...
(12)项目相关:Redis的集群了解吗?谈谈你对高可用的理解?
高可用的话我理解就是像我们以前来实现这一个服务的可用性,一般是通过我们的运维工程师来实现的。
比如说我们有个Redis的实例挂了,有用户反馈,然后我们运维的就加班加点就是去更新服务器,就是去启用一个新的Redis
实例,重启起来,然后把数据恢复进去,那么这是以前的做法。
那么如果我们要实现高可用,那么我们必须要实现就是可以一个自动切换
的这样一个功能。自动切换是怎么实现呢?
就是首先我们会有一台就是这主的Redis
,还有几台从的Redis
,因为我们主要我们的主的这个 Redis 会不断的就是去发数据,要让这从的节点
去同步我们这个主的Redis
里面的数据。
当我们的主节点这一个 Redis 它发生了这一个宕机之后,我们的 Redis 集群它里面有一种特殊的实例叫做哨兵实例,我们通过这个哨兵实例就可以就可以将这个主节点替换下来,就判断它下线以后我们再把从节点部署上去,这样的话就可以实现一个相当于实现节点的自动切换,尽管用户会感觉到有一段时间卡顿了,但是不需要运维人员手动实现切换了,那么那么我认为这就是一种高可用,像 MySQL 里面其实它也有这样的高可用的这种场景存在。
(13)用过消息队列吗(没)?谈谈你对消息队列的理解?
噢,这种东西它主要是实现一个我们这个生产者和消费者之间的解耦,就是说像我们传统的这一个,就是这一个网络处理流程中都是有一个生产者,然后生产者去调用我们这个消费者,这样的话它其实就形成了一条链路
,那么在这条链路上如果说我们的消费者没有消费完成,我们生产者它也是处于一个阻塞的状态的,但是实际上这个生产者其实可以不等待消费者消费完再生产的。
那么我们引入这个消息队列,相当于在他们之间架了一个桥梁,就是我们生产者不断的往里面去放数据,然后因为我们消费者就从里面取数据,当没有数据的时候,我们消费者就可以就会处于一个阻塞状态,可以等待,比如说等待我们生产者给他发消息,或者是我们这个消费者先暂时自旋,就是不断去自旋看它里面有没有数据。主要是实现这一个解耦的,就是避免它这个消费的速率或者生产的速率太低,主要我觉得是来做这件事情的,同时这个队列也可以实现一个异步通信之类的效果
(14)讲讲QPS怎么测的,简历上的数据是怎么来的
它其实有一个叫做Jmeter的工具,像这个的工具的话就是专门用来做性能测试。
一开始这个项目的版本,在实现防止超卖的问题的时候,是完全使用MySQL来实现的,那么一开始他这个QPS其实就 70- 80这样的非常低的这一个QPS,因为本身MySQL性能就比较差,而且在程序中还用到了悲观锁。
然后在引入我们这个Stream队列,引入这个我们这个异步消费的这样一个机制之后,它的我们看到的其实它的QPS 能够达到 400 到 500 左右,大概是大概当时是这么测出来的。
#如何判断面试是否凉了#