基础面经札记5

某公司面试题

1、讲一个项目的难点,以及怎么解决?(讲了实习时候的项目)

2、讲一下服务发现、服务注册、负载均衡以及相关实现

服务注册 :服务的提供者在中央注册表中注册其服务位置的过程。通常注册其主机和端口,有时还注册认证凭证,协议,版本号和或环境信息。

当一个微服务启动的时候,必须主动向服务注册中心注册其服务地址,以供其他微服务查询调用。

服务发现 :客户端应用程序查询中央注册表以了解服务位置的过程。

注册中心:维护中央注册表的角色被称为服务注册平台或者服务注册中心。

负载均衡:当一台服务器的单位时间内的访问量越大时,服务器压力就越大。为了避免服务器崩溃,让用户有更好的体验,通过负载均衡的方式来分担服务器压力。
我们可以建立很多很多服务器,组成一个服务器集群,当用户访问网站时,先访问一个中间服务器,在让这个中间服务器在服务器集群中选择一个压力较小的服务器,然后将该访问请求引入该服务器。如此以来,用户的每次访问,都会保证服务器集群中的每个服务器压力趋于平衡,分担了服务器压力,避免了服务器崩溃的情况。

负载均衡的策略机制

  1. 轮询(默认):请求依次轮流往每个应用服务器上进行分配,分配策略比较简单;
    缺点:不均匀,可能会出现,某些服务器接受的请求较重,负载压力重,有些负荷小,不可控。另外服务器之间需要进行session同步。
  2. 权重轮询:权重越高,进入的几率越大;
    优点:可以根据情况进行调整。可控,仍然需要进行session同步。
  3. IP-Hash:优点:无需进行session同步,固定IP会固定访问一台服务器。
    缺点:恶意攻击,会造成某台服务器压垮。提供的服务不同,面向的地区不同,IP可能会出现集中,造成不均匀,不可控。
  4. Fair:相当于自适应,会根据服务器处理请求的速度进行负载均衡分配。处理请求最早结束的,拿到下一个请求。看上去是不是很好。但是一般都不使用,说是考虑到网络不稳定因素。还有待研究。这种也需要进行session同步。
  5. URL-Hash:是根据URL进行hash,这样某些请求永远打某台服务器。利于利用服务器的缓存,但是可能由于URL的哈希值分布不均匀,以及业务侧重造成某些服务器压力大,某些负荷低。这种也需要进行session同步。

实现负载均衡有多种方式

  1. 软件负载均衡:比如常见的Nginx、LVS。
  2. 硬件负载均衡;买相应硬件。
  3. DNS负载均衡:通过DNS域名解析的方式,使多个服务器IP对应一个域名。

3、为什么需要服务发现?为什么选择zookeeper?

如果是固定的服务数量或物理机之类的,只需要简单配置文件就可以。
但在微服务架构中,服务众多,服务之间的相互依赖也错综复杂。
无论是服务主动停止,意外挂掉,还是因为流量增加对服务实现进行扩容,这些服务数据或状态上的动态变化,都需要尽快的通知到被调用方,被调用方才采取相应的措施。
因此,对于服务注册与发现要实时管理者服务的数据与状态,包括服务的注册上线、服务主动下线,异常服务的剔除。
所以需要服务发现。

客户端发现或服务器端发现

  1. 客户端发现:服务使用者保留提供者的所有位置,并在各个位置之间平衡请求的负载。优点:注册表是唯一的另一个组件。缺点:需要为系统中使用的不同语言/框架实现服务发现客户端。
  2. 服务器端发现:使用者向负载均衡器发送请求,负载均衡器从注册表中查询并确定要发送到的提供程序的位置。优点:与语言/框架无关。缺点:现需要管理另一个运动部件-负载平衡器。

服务发现的核心就是 注册中心

注册中心需求分析:

注册中心本身是一个服务器;
注册中心对服务提供者提供的一些功能;
注册中心对服务消费者提供的另一些功能

那么,从上面的分析中了解到:
注册中心的真正核心在于能够对APP服务器池进行轮询和负载均衡
现在需要先基本实现下 负载均衡 及 轮询

Zookeeper:是一个为分布式应用提供协调服务的Apache项目。
Dubbo是一个Apache的Java RPC远程程序调用框架,说白了就是分布式架构中不同服务器上的应用之间进行程序的调用。

Zookeeper:特性
1.顺序一致性:从一个客户端发起的事务请求,最终都会严格按照其发起顺序被应用到Zookeeper中;对于来自客户端的每个更新请求,Zookeeper都会分配一个全局唯一的递增ID(zxid),这个ID反映了所有事务请求的先后顺序。
2.原子性:所有事务请求的处理结果在整个集群中所有机器上都是一致的
3.最终一致性:所有客户端看到的服务端数据模型都是一致的;
4.可靠性:一旦服务端成功应用了一个事务,则其引起的改变会一直保留,直到被另外一个事务所更改,如果消息被到一台服务器接受,那么它将被所有的服务器接受。
5.实时性:一旦一个事务被成功应用后,Zookeeper可以保证客户端立即可以读取到这个事务变更后的最新状态的数据。
6.等待无关(wait-free):慢的或者失效的client不得干预快速的client的请求,使得每个client都能有效的等待。

由于Zookeeper的所有更新和删除都是基于事务的,所以其在读多写少的应用场景中有着很高的性能表现。ZooKeeper将数据存全量储在内存中以保持高性能,并通过服务集群来实现高可用。

4、 容器云,云服务, K8S,Docker, 底层大概的架构嘛?怎么实现资源隔离?

虚拟机就是用来模拟计算机系统的软件,让使用者可以在一台计算机上运行看似多台计算机的设备。

但是,虚拟机会占用大量系统资源。每个虚拟机不仅要运行一个完整的操作系统,还需要运行操作系统要运行的所有虚拟硬件。这样就会消耗大量的内存和 CPU 资源。与运行单独的物理计算机相比,这样是比较经济的;但对于某些应用程序而言却是很浪费的。

容器(Container)**是一种更轻量级,更灵活的虚拟化处理方式,它将一个应用程序所需的一切打包在一起。容器包括所有代码,各种依赖甚至操作系统,这让应用程序几乎在任何地方都可以运行。因此它的诞生,解决了一个重要问题:如何确保应用程序从一个环境移动到另一个环境的正确运行。它只是虚拟了操作系统,而不像虚拟机一样去虚拟底层计算机。

对比虚拟机,容器有哪些特点呢?

  1. 可移植性
  2. 轻量级
  3. 成本更低

Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows 机器上,也可以实现虚拟化。

Docker是应用最为广泛的容器技术,通过打包镜像,启动容器来创建一个服务。但是随着应用越来越复杂,容器的数量也越来越多,由此衍生了管理运维容器的重大问题,而且随着云计算的发展,云端最大的挑战,容器在漂移。在此业务驱动下,k8s问世,提出了一套全新的基于容器技术的分布式架构领先方案。

Kubernetes 是一个跨主机集群的 开源的容器调度平台,它可以自动化应用容器的部署、扩展和操作 , 提供以容器为中心的基础架构。

容器云是基于 Docker 的分布式计算资源网,提供多线网络,融合微服务、开发、运维一体化,大幅降低分布式计算资源构建复杂度,大幅降低使用成本。

docker基本概念
镜像(Image)
Docker镜像是一个特殊的文件系统,除了提供容器运行时所需要的程序、库、资源、配置文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷,环境变量,用户等)。镜像不包含任何动态数据,其内容在构建之后再也不会被改变。

容器(Container)
镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。

容器的实质就是进程,但与直接在宿主执行的进程不同,容器进程运行属于自己的独立的命名空间。因此容器可以拥有自己的root文件系统、自己的网络配置、自己的进程空间,甚至自己的用户ID空间。容器内的进程是运行在一个隔离环境里,使用起来,就好像是一个独立于宿主的系统下操作一样,这种特性使得容器封装的应用比直接在宿主运行更加安全。

镜像使用的是分层存储,容器也是如此。每一容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为容器存储层。

容器存储层的生存周期也和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。

仓库(Repository)
镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry(或者自建)就是这样的一种服务。

一个Docker Registry重病可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。

5、说一下有什么常用的IO模型?

1.阻塞IO模型;2.非阻塞IO模型 ;3.IO复用模型;4.信号驱动IO;5.异步IO模型
注意:前四种都是同步,只有最后一种才是异步IO。

概念详解
阻塞:阻塞调用是指调用结果返回之前,当前线程会被挂起(线程进入非可执行状态,在这个状态下,cpu不会给线程分配时间片,即线程暂停运行)。函数只有在得到结果之后才会返回。

非阻塞:非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。

同步:所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。也就是必须一件一件事做,等前一件做完了才能做下一件事。

异步:异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。

同步IO和异步IO的区别就在于:数据拷贝的时候进程是否阻塞
阻塞IO和非阻塞IO的区别就在于:应用程序的调用是否立即返回

阻塞I/O模型:进程会一直阻塞,直到数据拷贝完成。
应用程序调用一个IO函数,导致应用程序阻塞,等待数据准备好。 如果数据没有准备好,一直等待数据准备好了,从内核拷贝到用户空间,IO函数返回成功指示。

非阻塞IO模型:同步非阻塞IO是在同步阻塞IO的基础上,将socket设置为NONBLOCK。这样做用户线程可以在发起IO请求后可以立即返回。由于socket是非阻塞的方式,因此用户线程发起IO请求时立即返回。但并未读取到任何数据,用户线程需要不断地发起IO请求,直到数据到达后,才真正读取到数据,继续执行

IO复用模型:主要是select和epoll;对一个IO端口,两次调用,两次返回,比阻塞IO并没有什么优越性;关键是能实现同时对多个IO端口进行监听;
I/O复用模型会用到select、poll、epoll函数,这几个函数也会使进程阻塞,但是和阻塞I/O所不同的的,这两个函数可以同时阻塞多个I/O操作。而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操作函数。

异步IO模型:数据拷贝的时候进程无需阻塞。
当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者的输入输出操作。

同步IO引起进程阻塞,直至IO操作完成。
异步IO不会引起进程阻塞。
IO复用是先通过select调用阻塞。

select、poll、epoll简介
epoll跟select都能提供多路I/O复用的解决方案。在现在的Linux内核里有都能够支持,其中epoll是Linux所特有,而select则应该是POSIX所规定,一般操作系统均有实现

select:select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理。这样所带来的缺点是:

  1. 单个进程可监视的fd数量被限制,即能监听端口的大小有限。
  2. 对socket进行扫描时是线性扫描,即采用轮询的方法,效率较低:
  3. 需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大

poll:poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。
它没有最大连接数的限制,原因是它是基于链表来存储的,但是同样有一个缺点:

  1. 大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。
  2. poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。

epoll:epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就需态,并且只会通知一次。还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知
epoll的优点:

  1. 没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口);
  2. 效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数;即Epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll。
  3. 内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销。

总结

综上,在选择select,poll,epoll时要根据具体的使用场合以及这三种方式的自身特点。
1、表面上看epoll的性能最好,但是在连接数少并且连接都十分活跃的情况下,select和poll的性能可能比epoll好,毕竟epoll的通知机制需要很多函数回调。
2、select低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善。

5、非阻塞IO优势?深入讲一下?再深入讲一下 网卡到epoll中间发生了什么?

非阻塞IO优势
单独比较速度的话,非阻塞IO并不会说特别有优势,快到那里去,但是在某些场景下,性能会稍比阻塞IO好。
如在并发情况下或网络连接比较多的情况下,如果是阻塞IO,那么每个请求在没得到响应请不会返回,会一直等待,这样会损耗服务器的性能;如果是非阻塞IO,则没有得到响应的情况下,会先返回,之后再重新发起请求,这样可以减少并发数和网络连接,减轻服务器的压力。

网卡到epoll的过程

  1. 网卡接收到数据,把数据写入到内存后,网卡向 CPU 发出一个中断信号;
  2. 触发中断(硬件产生信号需要 CPU 立马做出回应,不然数据就丢失了,所以它的优先级很高);
  3. CPU 中断掉正在执行的程序,去做出响应;主要做两件事,先将网络数据写入到对应 Socket 的接收缓冲区里面,再唤醒进程,重新将进程 A 放入工作队列中。
  4. 当 CPU 完成对硬件的响应后,再重新执行用户程序

epoll详见这篇文章

6、系统设计,让我设计一个秒杀系统,难点跟解决方案?

秒杀系统的主要三个难点
1.瞬时并发;
2.超卖;
3.性能;

针对瞬时并发:缓存、异步、限流

前端
1.页面静态化,然后提前刷新到CDN节点,通过CDN节点的页面缓存来缓解访问压力和网络带宽;
2.禁止重复提交:用户提交之后按钮置灰,禁止重复提交
3.用户限流:在某一时间段内只允许用户提交一次请求,比如可以采取IP限流

后端

1.服务器上,限流,削峰,异步处理,内容缓存;

  1. 限流: 只有少部分用户能够秒杀成功,所以要限制大部分流量,只允许少部分流量进入服务后端。
  2. 削峰:对于秒杀系统瞬时会有大量用户涌入,所以在抢购一开始会有很高的瞬间峰值。高峰值流量是压垮系统很重要的原因,所以如何把瞬间的高流量变成一段时间平稳的流量也是设计秒杀系统很重要的思路。削峰从本质上来说就是更多地延缓用户请求的发出,以便减少和过滤掉一些无效请求,它遵从“请求数要尽量少”的原则。实现削峰的常用的方法有利用缓存和消息中间件等技术。操作思路:排队、答题、分层过滤等。
  3. 异步处理:秒杀系统是一个高并发系统,采用异步处理模式可以极大地提高系统并发量,其实异步处理就是削峰的一种实现方式。
  4. 内存缓存:秒杀系统最大的瓶颈一般都是数据库读写,由于数据库读写属于磁盘IO,性能很低,如果能够把部分数据或业务逻辑转移到内存缓存,效率会有极大地提升。

针对超卖
采用预扣库存。买家下单后,库存为其保留一定的时间(如 10 分钟),超过这个时间,库存将会自动释放,释放后其他买家就可以继续购买。在买家付款前,系统会校验该订单的库存是否还有保留:如果没有保留,则再次尝试预扣;如果库存不足(也就是预扣失败)则不允许继续付款;如果预扣成功,则完成付款并实际地减去库存。

针对性能
合理设置线程数
合理设置缓存服务器数
合理设置服务器集群数

7、如何看待代码设计?怎么看待可读性,你觉得什么是可读性?

8、你觉得为什么要设计模式

设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。
这些解决方案是众多软件开发人员经过相当长一段时间的试验和错误总结出来的。

设计模式能够帮助我们在写代码的时候,解决不少很多问题,能让我们开发的项目更加合理。

好处:

  1. 重用设计和代码
  2. 提高扩展性,大量使用面向接口编程,预留扩展插槽,新的功能或特性很容易加入到系统中来;
  3. 提高灵活性,通过组合提高灵活性,可允许代码修改平稳发生,对一处修改不会波及到其他模块;
  4. 提高开发效率,正确使用设计模式,可以节省大量的时间;

9、还有另外的人觉得还是需要注释,有注释的才是好代码。你怎么看待这两种不同的观点?

10、CAP 理论说一下?

CAP定理:指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。
CAP 原则指:这三个要素最多只能同时实现两点,不可能三者兼顾。
一致性(C):在分布式系统中的所有数据备份,在同一时刻数据是否相同。(等同于所有节点访问同一份最新的数据副本)
可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
分区容忍性(P):以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。

CAP原则的精髓就是要么AP,要么CP,要么AC,但是不存在CAP。如果在某个分布式系统中数据无副本, 那么系统必然满足强一致性条件, 因为只有独一数据,不会出现数据不一致的情况,此时C和P两要素具备,但是如果系统发生了网络分区状况或者宕机,必然导致某些数据不可以访问,此时可用性条件就不能被满足,即在此情况下获得了CP系统,但是CAP不可同时满足 。

因此在进行分布式架构设计时,必须做出取舍。当前一般是通过分布式缓存中各节点的最终一致性来提高系统的性能,通过使用多节点之间的数据异步复制技术来实现集群化的数据一致性。

11、为什么需要线程池啊?坏处?如何排查线程死循环?

原因
因为每当有请求或者其他任务需要时,每次都新建线程,开销比较大,所以引进了线程池,有需要就从线程池里面获取,用完就放回的线程池,重复利用已创建的线程降低线程创建和销毁造成的消耗。提高响应速度。 当任务到达时,任务可以不需要等到线程创建就能立即执行。

线程池是一种异步化技术,通过预先创建线程/异步处理来提高响应速度。同时通过统一调配线程资源,可以降低线程的重复创建问题,提高线程的利用率,中心化管理有利于对资源的有效控制,防止滥用。

坏处

  1. 可能造成死锁,如所有池线程都在执行已阻塞的等待队列中另一任务的执行结果的任务,但这一任务却因为没有未被占用的线程而不能运行。
  2. 可能线程泄漏,各种类型的线程池中一个严重的风险是线程泄漏,当从池中除去一个线程以执行一项任务,而在任务完成后该线程却没有返回池时,会发生这种情况。发生线程泄漏的一种情形出现在任务抛出一个 RuntimeException 或一个 Error 时。如果池类没有捕捉到它们,那么线程只会退出而线程池的大小将会永久减少一个。当这种情况发生的次数足够多时,线程池最终就为空,而且系统将停止,因为没有可用的线程来处理任务。

如何排查线程死循环
1.查看java进程:使用jps 或者ps -ef 查找到相应的java进程或者使用top查看机器占用cpu比例高的进程
2.使用top -H查看线程
3.将线程号:转换成十六进制
4.查看dump文件或者使用jstack | grep <十六进制子进程号>

12、redis 数据类型,zset 底层数据结构?

数据类型
string
list
hash
set
zset(sort set)

zset 底层数据结构
zset结构使用的是hash加上跳跃表的设计

跳表全称为跳跃列表,它允许快速查询,插入和删除一个有序连续元素的数据链表。
跳表是一个多层次的链表,且每一层链表中的元素是前一层链表元素的子集。(也可说上一层是下一层的索引)

跳表的主要思想就是逐渐建立索引,加速查找与插入。

zset底层的存储结构包括ziplist或skiplist,在同时满足以下两个条件的时候使用ziplist,其他时候使用skiplist,两个条件如下:
有序集合保存的元素数量小于128个
有序集合保存的所有元素的长度小于64字节
当ziplist作为zset的底层存储结构时候,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,第二个元素保存元素的分值。
当skiplist作为zset的底层存储结构的时候,使用skiplist按序保存元素及分值,使用dict来保存元素和分值的映射关系。

13、红黑树插入查找时间复杂度?

如果二叉排序树是平衡的,则n个节点的二叉排序树的高度为Log2n+1,其查找效率为O(Log2n),近似于折半查找。如果二叉排序树完全不平衡,则其深度可达到n,查找效率为O(n),退化为顺序查找。一般的,二叉排序树的查找性能在O(Log2n)到O(n)之间。因此,为了获得较好的查找性能,就要构造一棵平衡的二叉排序树。

红黑树的主要目的是实现一种平衡二叉树,这样可以达到最优的查询性能,时间复杂度为O(logn)、 n为数据个数。

红黑树查找,因为红黑树是一颗二叉平衡树,并且查找不会破坏树的平衡,所以查找跟二叉平衡树的查找无异。正由于红黑树总保持黑色完美平衡,所以它的查找最坏时间复杂度为O(2lgN),也即整颗树刚好红黑相隔的时候,能有这么好的查找效率得益于红黑树自平衡的特性。

红黑树插入操作包括两部分工作:一查找插入的位置;二插入后自平衡。
而在期间主要时间损耗集中在插入的查找上,所以平均的时间复杂度是O(logn)

14、整个视频通话流程说一下?

15、static关键字作用

1、被static修饰的变量属于类变量,可以通过类名.变量名直接引用,而不需要new出一个类来;
2、被static修饰的方法属于类方法,可以通过类名.方法名直接引用,而不需要new出一个类来;
3、被static修饰的变量、被static修饰的方法统一属于类的静态资源,是类实例之间共享的;

被static修饰的变量、被static修饰的方法统一属于类的静态资源,是类实例之间共享的,换言之,一处变、处处变。JDK把不同的静态资源放在了不同的类中而不把所有静态资源放在一个类里面,很多人可能想当然认为当然要这么做,但是是否想过为什么要这么做呢?个人认为主要有三个好处:

1、不同的类有自己的静态资源,这可以实现静态资源分类。比如和数学相关的静态资源放在java.lang.Math中,和日历相关的静态资源放在java.util.Calendar中,这样就很清晰了

2、避免重名。不同的类之间有重名的静态变量名、静态方法名也是很正常的,如果所有的都放在一起不可避免的一个问题就是名字重复,这时候怎么办?分类放置就好了。

3、避免静态资源类无限膨胀,这很好理解。

16、内联函数

用关键字inline修饰的函数就是内联函数。关键字在函数声明和定义的时候都要加上,不写系统还是会当成常规函数。

内联函数与一般函数的区别:

  1. 内联含函数比一般函数在前面多一个inline修饰符
  2. 内联函数是直接复制“镶嵌”到主函数中去的,就是将内联函数的代码直接放在内联函数的位置上,这与一般函数不同,主函数在调用一般函数的时候,是指令跳转到被调用函数的入口地址,执行完被调用函数后,指令再跳转回主函数上继续执行后面的代码;而由于内联函数是将函数的代码直接放在了函数的位置上,所以没有指令跳转,指令按顺序执行
  3. 一般函数的代码段只有一份,放在内存中的某个位置上,当程序调用它是,指令就跳转过来;当下一次程序调用它是,指令又跳转过来;而内联函数是程序中调用几次内联函数,内联函数的代码就会复制几份放在对应的位置上
  4. 内联函数一般在头文件中定义,而一般函数在头文件中声明,在cpp中定义

利与弊
利:避免了指令的来回跳转,加快程序执行速度
弊:代码被多次复制,增加了代码量,占用更多的内存空间

什么时候使用内联函数
1.函数本身内容比较少,代码比较短,函数功能相对简单
2.函数被调用得频繁,不如循环中的函数

17、TCP/UDP特点

1、连接方面区别
TCP面向连接(如打电话要先拨号建立连接)。
UDP是无连接的,即发送数据之前不需要建立连接。

2、安全方面的区别
TCP提供可靠的服务,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达。
UDP尽最大努力交付,即不保证可靠交付。

3、传输效率的区别
TCP传输效率相对较低。
UDP传输效率高,适用于对高速传输和实时性有较高的通信或广播通信。

4、连接对象数量的区别
TCP连接只能是点到点、一对一的。
UDP支持一对一,一对多,多对一和多对多的交互通信。

18、TCP的拥塞控制

拥塞控制:拥塞控制是TCP在传输时尽可能快的将数据传输,并且避免拥塞造成的一系列问题。是可靠性的保证,同时也是维护了传输的高效性。

TCP的四种拥塞控制算法

  1. 慢开始: 发送方维持一个叫做拥塞窗口cwnd(congestion window)的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送方让自己的发送窗口等于拥塞窗口,另外考虑到接受方的接收能力,发送窗口可能小于拥塞窗口。慢开始算法的思路就是,不要一开始就发送大量的数据,先探测一下网络的拥塞程度,也就是说由小到大逐渐增加拥塞窗口的大小。
  2. 拥塞控制:拥塞避免算法让拥塞窗口缓慢增长,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍,这样拥塞窗口按线性规律缓慢增长。
  3. 快重传:快重传要求接收方在收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方)而不要等到自己发送数据时捎带确认。快重传算法规定,发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待设置的重传计时器时间到期。
  4. 快恢复:快重传配合使用的还有快恢复算法,有以下两个要点:
    ①当发送方连续收到三个重复确认时,就执行“乘法减小”算法,把ssthresh门限减半。但是接下去并不执行慢开始算法。
    ②考虑到如果网络出现拥塞的话就不会收到好几个重复的确认,所以发送方现在认为网络可能没有出现拥塞。所以此时不执行慢开始算法,而是将cwnd设置为ssthresh的大小,然后执行拥塞避免算法。

19、Linux下的常用命令

查看指令信息
man
文件操作
cd,pwd,ls,mkdir,rmdir,rm,cp,ln,touch
网络
netsttat,nc,tcpdump,telnet,ssh
用户管理
su,whoami,id,adduser,useradd,deluser
文件内容搜索
cat,grep,more,less
系统监控
top,df
进程相关
ps,kill
文本编辑器
vim

查看Linux系统版本
uname -a 显示系统名、节点名称、操作系统的发行版号、操作系统版本、运行系统的机器 ID 号。
uname -r 显示当前系统发行版号

20、huffman树

哈夫曼树称最优二叉树,是一种带权路径长度最短的二叉树。
哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。
所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。

21、JVM

JVM即Java虚拟机,是一种抽象计算机,它有一个指令集,在运行时操作各种内存区域。虚拟机有很多种,不同厂商提供了不同实现,只要遵循虚拟机规范即可,目前我们所说的虚拟机一般指的是Hot Spot。JVM对Java语言一无所知,只知道一种特定的二进制格式,即类文件格式,我们写好的程序最终交给JVM执行的时候会被编译成二进制格式,JVM只认识二进制格式,所以任何语言只要编译后的格式符合要求,都可以在JVM上运行。

22、mysql慢查询

MySQL的慢查询,全名是慢查询日志,是MySQL提供的一种日志记录,用来记录在MySQL中响应时间超过阀值的语句。(就是运行的比较慢的sql语句)

23、排序算法的时间复杂度

在这里插入图片描述

24、多线程和多进程的区别

进程
程序执行的实例
拥有独立的地址内存空间
系统分配资源和调度的基本单位
线程
线程是进程的一个实体,进程的执行路径
没有独立的地址内存空间,依附于进程
多个线程共享所属进程资源
CPU调度的基本单位
线程只拥有在运行中必不可少的资源(如程序计数器,栈等)

多线程和多进程的区别

  1. CPU利用率:多进程中占用内存多,切换复杂,CPU利用率低;多线程中占用内存少,切换简单,CPU利用率高。
  2. 编程:多进程中编程简单,调试简单;多线程中编程复杂,调试复杂。
  3. 影响关系:多进程中进程间不会相互影响;多线程中一个线程挂掉将导致整个进程挂掉。

25.TCP 三次握手和互相交换的数据是什么

第一次握手:客户端向服务端发送出连接包(syn),然后进入syn_send状态,等待服务器的确认;
第二次握手:服务端收到客户端的连接包(syn),向客户端发送出确认包(ack),同时也发出向客户端的连接包,即发送了(syn+ack)包,然后进入syn_recevie状态;
第三次握手:客户端接收到服务端发来的(syn+ack)包后,向服务端发送出确认包(ack = k+1),发送完后进入到连接状态(ESTABLISHED),完成三次握手,两端开始传输数据。

26、在哪里使用的TCP,怎么用的

Tcpip是一个网络通信模型,以及一整个网络传输协议家族,为互联网的基础通信架构。
Tcpip常被通称为TCP/IP协议族,简称TCP/IP。tcpip协议中,Tcp协议在传输层,ip协议在网际层。

TCP/IP协议是用来提供点对点的链接机制,将数据应该如何封装、定址、传输、路由以及在目的地如何接收,都加以标准化。它将软件通信过程抽象化为四个抽象层,采取协议堆栈的方式,分别实现出不同通信协议。

TCP/IP分为tcp协议和ip协议:
TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议.
ip协议是互联网协议地址,缩写为IP地址,是分配给用户上网使用的网际协议 的设备的数字标签。常见的IP地址分为IPv4与IPv6两大类。

27、工厂模式

工厂模式是最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。
因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A()
工厂模式也是用来创建实例对象的,在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象,这可以给系统带来更大的可扩展性和尽量少的修改量。

28.讲一下消息队列

消息队列:是指利用高效可靠的消息传递机制进行与平台无关的数据交流,并基于数据通信来进行分布式系统的集成。
ActiveMQ 是Apache出品,,能力强劲的开源消息总线。

特点(作用)
应用解耦
异步通信
流量削峰
(海量)日志处理
消息通讯

应用场景
根据消息队列的特点,可以衍生出很多场景,或者说很多场景都能用到。

1.异步通信
​ 注册时的短信、邮件通知,减少响应时间;

2.应用解耦
​ 信息发送者和消息接受者无需耦合,比如调用第三方;

3.流量削峰
例如秒杀系统;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值