蚂蚁集团面试题

1 Dubbo

1.1 服务调用超时问题怎么解决?

dubbo源码分析(二):超时原理以及应用场景

1.2 Dubbo支持哪些序列化方式?

  • Hessian 序列化:是修改过的 hessian lite,默认启用
  • json 序列化:使用 FastJson 库
  • java 序列化:JDK 提供的序列化,性能不理想
  • dubbo 序列化:未成熟的高效 java 序列化实现,不建议在生产环境使用

1.3 Dubbo和SpringCloud的关系?

dubbo springcloud
消费者,生产者,注册中心,管理中心 消费者,生产者,注册中心,管理中心,短路器,分布式配置,消息总线,服务追踪
Netty这样的NIO框架,是基于TCP协议传输的,配合以Hession序列化完成RPC 基于Http协议+rest接口调用远程请求,相对来说,Http请求会有更大的报文,占的带宽也会更多。
dubbo类似组装机,开发相对复杂 springcloud类似一体机,一站式开发,相对简单

1.4 Dubbo的架构设计?一共划分了哪些层?

  • 服务接口层(Service):该层是与实际业务逻辑相关的,根据服务提供方和服务消费方的业务设计对应的接口和实现
  • 配置层(Config):对外配置接口,以 ServiceConfig 和 ReferenceConfig 为中心
  • 服务代理层(Proxy):服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton
  • 服务注册层(Registry):封装服务地址的注册与发现,以服务 URL 为中心
  • 集群层(Cluster):封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心
  • 监控层(Monitor):RPC 调用次数和调用时间监控
  • 远程调用层(Protocol):封将 RPC 调用,以 Invocation 和 Result 为中心,扩展接口为 Protocol、Invoker、Exporter
  • 信息交换层(Exchange):封装请求响应模式,同步转异步,以 Request 和 Response 为中心
  • 网络传输层(Transport):抽象 mina 和 netty 为统一接口,以 Message 为中心

1.5 Dubbo的默认集群容错方案?

  • Failover Cluster,失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。

  • Failfast Cluster,快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。

  • Failsafe Cluster,失败安全,出现异常时,直接忽略。通常用于写入日志等。

  • Failback Cluster,失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知等。

  • Forking Cluster,并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks 来设置最大并行数。

  • Broadcast Cluster,广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存本地资源信息。

    默认 Failover Cluster

1.6 Dubbo使用的是什么通信框架?

1.7 Dubbo的主要应用场景?

1.8 Dubbo服务注册与发现的流程?流程说明。

1.9 Dubbo的集群容错方案有哪些?

1.10 Dubbo的四大组件

1.11 Dubbo在安全机制方面是如何解决的

1.12 Dubbo和SpringCloud的区别?

1.13 Dubbo支持哪些协议,每种协议的应用场景,优缺点?

1.14 Dubbo的核心功能有哪些?

1.15 Dubbo的注册中心集群挂掉,发布者和订阅者之间还能通信么?

1.16 Dubbo集群的负载均衡有哪些策略

1.17 为什么需要服务治理?

1.18 Dubbo超时时间怎样设置?

2 ElasticSearch

2.1 你们公司的ES集群,一个node一般会分配几个分片?

2.2 Elasticsearch是如何实现Master选举的?

2.3 你是如何做写入调优的?

2.4 如何避免脑裂?

2.5 Elasticsearch对于大数据量(上亿量级)的聚合如何实现?

2.6 ES主分片数量可以在后期更改吗?为什么?

2.7 如何监控集群状态?

2.8 ElasticSearch中的副本是什么?

2.9 ES更新数据的执行流程?

2.10 shard里面是什么组成的?

2.11 ElasticSearch中的分析器是什么?

2.12 什么是脑裂?

2.13 客户端在和集群连接时,如何选择特定的节点执行请求的?

2.14 Elasticsearch中的倒排索引是什么?

2.15 什么是索引?索引(名词) 一个索引(index)

2.16 详细描述一下Elasticsearch更新和删除文档的过程

3 JVM

3.1 JVM参数主要有⼏种分类

IBM 微软的 hotpot

3.2 Java中会存在内存泄漏吗,简述一下。

会出现内存泄漏,当垃圾回收器清理内存后,现有内存使用量+申请新增内存>最大使用内存时,会出现内存泄漏

主要场景包括以下两种

  1. 机器内存不够用
  2. 内存分配不合理

3.3 Java虚拟机是如何判定两个Java类是相同的?

在新建对象的过程中,需要新通过类加载,而在类加载中通过 双亲委派机制 来判断两个类是否相同

当某个加载器需要加载某个.class 文件时,他首先把这个任务委托过他的上一级加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。

下面是类加载的过程

用于自定义类加载器 –> 系统类加载器 –> 扩展类加载 –> 启动类加载

3.4 Java 中都有哪些引用类型

  1. __强引用(strongreference)__就是指在程序代码之中普遍存在的,类似“Object obj=new Object()” 这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象实例。
  2. __软引用(softreference)__是用来描述一些还有用但并非必需的对象。对于软引用关联着的对象, 在系统将要发生内存溢出异常之前,将会把这些对象实例列进回收范围之中进行 第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。在 JDK 1.2 之后,提供了 SoftReference 类来实现软引用。
  3. __弱引用(weakreference)__也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱 引用关联的对象实例只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时, 无论当前内存是否足够,都会回收掉只被弱引用关联的对象实例。在 JDK 1.2 之 后,提供了 WeakReference 类来实现弱引用。
  4. __虚引用(phantomreference)__也称为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象 实例是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用 来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象 实例被收集器回收时收到一个系统通知。在 JDK 1.2 之后,提供了 PhantomReference 类来实现虚引用

3.5 在 Java 中,对象什么时候可以被垃圾回收?

当对象被判定已经“死去”时,对象可以被垃圾回收,判断对象是否存活可通过以下算法

  1. __引用计数算法:__通过引用计数器,当被引用+1,当失去引用-1,计数为零的引用被清理
  2. 可达性分析算法: 通过跟对象作为起始点,从这个节点开始向下搜索,搜索过程所走的路径称为“引用链”,如果某个对象的到 __GC Roots __间没有连接,则说明该对象不能再被引用

3.6 StackOverflow异常有没有遇到过?一般你猜测会在什么情况下被触发?

栈内存溢出。栈内存保存的信息包括:函数地址、函数参数、局部变量等

出现__栈内存溢出的常见原因有2个__:

  1. 函数调用层次过深,每调用一次,函数的参数、局部变量等信息就压一次栈
  2. 局部静态变量体积太大

解决办法大致说来也有两种

  1. 增加栈内存的数目
  2. 使用堆内存增加栈内存方法

3.7 堆空间分哪些部分?以及如何设置各个部分?

新生代,老年代

其中新生代,分为 伊甸,to,from 按照 8:1:1 的比例进行分割

3.8 什么是栈帧?栈帧存储了什么?

栈帧是方法运行期最重要的基本数据结构

存储包括:局部变量表,操作数栈,动态连接和方法返回地址

3.9 如何设置参数生成GC日志?

-XX:+PrintGC

3.10 GC 是什么?为什么要有 GC?

Garbage Collection 垃圾回收系统,

java提供的gc功能可以自动检测对象是否超出通过垃圾回收系统,由系统自动触发垃圾回收。java没有提供释放垃圾已分配内存的显示操作方法

3.12 使用过哪些jdk命令,并说明各个的作用是什么

jps:__用于显示__指定系统内所有HotSpot虚拟机进程,并且能显示虚拟机执行主类以及本地虚拟机唯一ID

jstat:__用于__监视虚拟机各种运行状态信息的工具,可以显示本地或者远程的虚拟机进程类装载、内存、GC、JIT等运行数据,在没有GUI图像界面的服务器上,主要就是用它在运行期定位性能问题

jmap:__用于生成堆转储快照(heapdump或dump文件),说白了就是把java堆使用情况快照一份导出来供我们查看,__用来排查问题

__jhat:__这个就是和jmap搭配使用的,jmap导出来的堆快照文件用jhat 打开分析

jstack:__用于生成虚拟机当前时刻线程快照(threaddump或javacore)。__主要用来定位线程出现长时间停顿的原因,判断死锁啊,死循环的等。通过jstack就可知各线程的调用堆栈情况

__jinfo:__用来查看和调整虚拟机各项参数使用格式

3.13 JVM运行时数据区区域分为哪⼏部分?

堆,方法区,本地方法区,计数器,栈

3.14 是否了解类加载器双亲委派模型机制和破坏双亲委派模型?

在类加载的过程中,会用到双亲委派机制,目的是为了保障.class 文件的唯一性,

他首先把这个任务委托过他的上一级加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。

双亲委派机制中类加载过程:自定义类加载器,程序类加载器,扩展类加载,系统类加载器

破坏双亲委派机制:通过继承 ClassLoader 并重写loadclass

3.15 逃逸分析有几种类型?

方法逃逸:对象的方法被外部方法所引用

线程逃逸:方法被外部线程访问到

3.16 -Xms这些参数的含义是什么?

设置堆内存

3.17 你知道哪几种垃圾收集器,各自的优缺点,重点讲下cms和G1,包括原理,流程,优缺点。

3.18 JVM的内存结构,Eden和Survivor比例是多少?

8:1:1

4 多线程/高并发

4.1 负载平衡的意义什么?

提高硬件的使用率,提升系统的容量

4.2 请说出同步线程及线程调度相关的方法?

线程调度相关方法

yield():线程让步,暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程。若队列中没有同优先级的线程,忽略此方法。

join() :当某个程序执行流中调用其他线程的 join() 方法时,调用线程将被阻塞,直到 join() 方法加入的 join 线程执行完为止,低优先级的线程也可以获得执行

sleep(long millis)(毫秒) : 令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队。

isAlive():判断线程是否还活着

线程同步:

synchronized 同步代码快,同步方法

4.3 关于epoll和select的区别,哪些说法 是正确的?(多选)

A. epoll 和 select 都是 I/O 多路复用的技术,都可以实现同时监听 多个I/O事件的状态。 B. epoll 相比 select 效率更高,主要是基于其操作系统支持的 I/O 事件通知机制,而select是基于轮询机制。 C. epoll支持水平触发和边沿触发两种模式。 D. select能并行支持I/O比较小,且无法修改。

4.4 启动一个线程是调用run()方法还是start()方法?

run() 方法,start() 中饱含 run() 方法

4.5 如何确保N个线程可以访问N个资源同时又不导致死锁?

多线程产生死锁需要四个条件,分别是互斥性,保持和请求,不可剥夺性还有要形成闭环,这四个条件缺一不可,只要破坏了其中一个条件就可以破坏死锁,其中最简单的方法就是线程都是以同样的顺序加锁和释放锁,也就是破坏了第四个条件

4.6 编写多线程程序的几种实现方式(换个问法:创建多线程的方式)?

继承 Thread 重写run函数

实现Runnable 重写run函数

实现Callable接口,重写call函数

4.7 线程和进程的区别?

线程是进程执行程序的最小单元

进程:一个程序一个进程

线程:一个进程包含多个线程

4.8 什么是线程池,有哪些常用线程池?

若干个线程放入同一个容器中,使用的时候从池中获取而不用自行创建,使用完毕不需 要销毁线程而是放回池中, 从而减少创建和销毁线程对象的开销

常用线程池:

__newSingleThreadExecutor: __创建一个单线程的线程池, 此线程池保证所有任务的执行顺序按照任务的 提交顺序执行。

__newFixedThreadPool: __创建固定大小的线程池, 每次提交一个任务就创建一个线程, 直到线程达到线 程池的最大大小。

newCachedThreadPool: 创建一个可缓存的线程池, 此线程池不会对线程池大小做限制, 线程池大小 完全依赖于操作系统(或者说 JVM) 能够创建的最大线程大小。

__newScheduledThreadPool: __创建一个大小无限的线程池, 此线程池支持定时以及周期性执行任务的需 求。

__newSingleThreadExecutor: __创建一个单线程的线程池。 此线程池支持定时以及周期性执行任务的需 求

4.9 什么是死锁?

是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进

多线程产生死锁需要四个条件,分别是互斥性,保持和请求,不可剥夺性还有要形成闭环,这四个条件缺一不可

4.10 怎么保证缓存和数据库数据的一致性?

  • Cache aside (旁路缓存 )

这是最常用最常用的pattern了。其具体逻辑如下:

  • 失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
  • 命中:应用程序从cache中取数据,取到后返回。
  • 更新:先把数据存到数据库中,成功后,再让缓存失效

注意,我们的更新是先更新数据库,成功后,让缓存失效。那么,这种方式是否可以没有文章前面提到过的那个问题呢?我们可以脑补一下。

一个是查询操作,一个是更新操作的并发,首先,没有了删除cache数据的操作了,而是先更新了数据库中的数据,此时,缓存依然有效,所以,并发的查询操作拿的是没有更新的数据,但是,更新操作马上让缓存的失效了,后续的查询操作再把数据从数据库中拉出来。而不会像文章开头的那个逻辑产生的问题,后续的查询操作一直都在取老的数据。

这是标准的design pattern,包括Facebook的论文《Scaling Memcache at Facebook》也使用了这个策略。为什么不是写完数据库后更新缓存?你可以看一下Quora上的这个问答《Why does Facebook use delete to remove the key-value pair in Memcached instead of updating the Memcached during write request to the backend?》,主要是怕两个并发的写操作导致脏数据。

那么,是不是Cache Aside这个就不会有并发问题了?不是的,比如,一个是读操作,但是没有命中缓存,然后就到数据库中取数据,此时来了一个写操作,写完数据库后,让缓存失效,然后,之前的那个读操作再把老的数据放进去,所以,会造成脏数据。

但,这个case理论上会出现,不过,实际上出现的概率可能非常低,因为这个条件需要发生在读缓存时缓存失效,而且并发着有一个写操作。而实际上数据库的写操作会比读操作慢得多,而且还要锁表,而读操作必需在写操作前进入数据库操作,而又要晚于写操作更新缓存,所有的这些条件都具备的概率基本并不大。

所以,这也就是Quora上的那个答案里说的,要么通过2PC或是Paxos协议保证一致性,要么就是拼命的降低并发时脏数据的概率,而Facebook使用了这个降低概率的玩法,因为2PC太慢,而Paxos太复杂。当然,最好还是为缓存设置上过期时间。

5 消息中间件

5.1 消费者获取消息有几种模式?

推送模式: consumer把轮询过程封装了,并注册到MessageListener监听器中,对于offset进行自动保存。取到消息后,唤醒MessageListener的consumeMessage()来消费,这种触发方法才会被调用的方式对用户而言感觉就像是被推送过来。

__拉取模式:__这种方法非常少用,它在取消息的过程中需要用户自己手动操作。首先在要消费的Topic中拿到MessageQueue的集合,遍历MessageQueue集合,然后针对每一个MessageQueue批量取消息,每取完一次,记录该队列下一次要取的开始offset,直到取完一整个MessageQueue,再换另一个MessageQueue。

5.2 RocketMQ的特点有哪些?

  1. 灵活可扩展性 RocketMQ 天然支持集群,其核心四组件(Name Server、Broker、Producer、Consumer)每一个都可以在没有单点故障的情况下进行水平扩展。
  2. 海量消息堆积能力 RocketMQ 采用零拷贝原理实现超大的消息的堆积能力,据说单机已可以支持亿级消息堆积,而且在堆积了这么多消息后依然保持写入低延迟。
  3. 支持顺序消息 可以保证消息消费者按照消息发送的顺序对消息进行消费。顺序消息分为全局有序和局部有序,一般推荐使用局部有序,即生产者通过将某一类消息按顺序发送至同一个队列来实现。
  4. 多种消息过滤方式 消息过滤分为在服务器端过滤和在消费端过滤。服务器端过滤时可以按照消息消费者的要求做过滤,优点是减少不必要消息传输,缺点是增加了消息服务器的负担,实现相对复杂。消费端过滤则完全由具体应用自定义实现,这种方式更加灵活,缺点是很多无用的消息会传输给消息消费者。
  5. 支持事务消息 RocketMQ 除了支持普通消息,顺序消息之外还支持事务消息,这个特性对于分布式事务来说提供了又一种解决思路。
  6. 回溯消费 回溯消费是指消费者已经消费成功的消息,由于业务上需求需要重新消费,RocketMQ 支持按照时间回溯消费,时间维度精确到毫秒,可以向前回溯,也可以向后回溯

5.3 kafka 同时设置了 7 天和 10G 清除数据,到第五天的时候消息达到了 10G,这个时候 kafka将如何处理?

这个时候 kafka 会执行数据清除工作,时间和大小不论那个满足条件,都会清空数据。

5.4 为何需要Kafka集群

提高信息吞吐量,

5.5 Kafka 数据存储设计

5.6 Kafka如何判断一个节点是否存活?

5.7 kafka消息发送的可靠性机制有几种 5.8 请详细说一下推送模式和拉取模式。 5.9 Kafka 与传统消息系统之间有三个关键区别

5.10 RocketMQ 由哪些角色组成?

生产者(Producer):负责产生消息,生产者向消息服务器发送由业务应用程序系统生成的消息。

消费者(Consumer):负责消费消息,消费者从消息服务器拉取信息并将其输入用户应用程序。

消息服务器(Broker):是消息存储中心,主要作用是接收来自 Producer 的消息并存储, Consumer 从这里取得消息。

名称服务器(NameServer):用来保存 Broker 相关 Topic 等元信息并给 Producer ,提供 Consumer 查找 Broker 信息。

5.12 Kafka的消费者如何消费数据 5.13 Kafka的优点 5.14 Kafka 的设计是什么样的呢? 5.15 说说你对Consumer的了解? 5.16 Kafka新建的分区会在哪个目录下创建

5.17 说一下Kafka消费者消费过程

消息的消费模型有两种:推送模型(push)和拉取模型(pull)

推送模型(push):

基于推送模型(push)的消息系统,由消息代理记录消费者的消费状态,消息代理在将消息推送到消费者后,标记这条消息为已消费,但这种方式无法很好地保证消息被处理,比如,消息代理把消息发送出去后,当消费进程挂掉或者由于网络原因没有收到这条消息时,就有可能造成消息丢失(因为消息代理已经把这条消息标记为已消费了,但实际上这条消息并没有被实际处理),如果要保证消息被处理,消息代理发送完消息后,要设置状态为“已发送”,只有收到消费者的确认请求后才更新为“已消费”,这就需要消息代理中记录所有的消费状态,这种做法显然是不可取的

拉取模型(pull):

Kafka采用拉取模型,由消费者自己记录消费状态,每个消费者互相独立地顺序读取每个分区的消息,有两个消费者(不同消费者组)拉取同一个主题的消息,消费者A的消费进度是3,消费者B的消费进度是6,消费者拉取的最大上限通过最高水位(watermark)控制,生产者最新写入的消息如果还没有达到备份数量,对消费者是不可见的,这种由消费者控制偏移量的优点是:消费者可以按照任意的顺序消费消息,比如:消费者可以重置到旧的偏移量,重新处理之前已经消费过的消息;或者直接跳到最近的位置,从当前的时刻开始消费

5.18 介绍下Kafka

kafka是一个 分布式的, __可分区__的, __可备份__的日志提交服务,它使用独特的设计实现了一个消息系统的功能。

5.19 什么情况会导致Kafka运行变慢?

  • cpu 性能瓶颈

  • 磁盘读写瓶颈

  • 网络瓶颈

5.20 kafka 可以脱离 zookeeper 单独使用吗?为什么?

kafka 不能脱离 zookeeper 单独使用,

因为 kafka 使用 zookeeper 管理和协调 kafka 的节点服务器。

5.21 使用 kafka 集群需要注意什么?

  • 集群的数量不是越多越好,最好不要超过 7 个,

  • 因为节点越多,消息复制需要的时间就越长,整个群组的吞吐量就越低。

  • 集群数量最好是单数,因为超过一半故障集群就不能用了,设置为单数容错率更高。

5.22 为什么使用消息队列呢(即优势所在)?

  1. 实现解耦;举个简单例子,快递小哥手上有很多快递需要配送,若一一通知收件人,估计快递小哥要疯掉,有了丰巢快递柜后,快递小哥可以集中将快递放到快递柜子里面,后面只需发送短信给收件人即可,这样以来快递柜子就充当了消息中间件,实现了解耦,两个人互不耽误。

  2. 实现冗余备份;当快递小哥兴致冲冲的拉来一车快递后,正准备投递到丰巢快递柜子,但不幸的是蜂巢快递柜子坏了,无法投递快递,快递小哥也没招,只能老老实实的去下一个丰巢快递柜子投递快递,若快递柜子又坏了,快递小哥还可以去下一个快递柜子,如此一来,实现了消息队列的冗余备份,保证你的快递不被丢失;

  3. 异步;如果楼下有你的快递,快递小哥需要一直在楼下等你取走快递后,快递小哥才能进行下一个快递的派单,这样以来快递小哥的效率极其低下,而有了快递柜子后,快递小哥只需要将你的快递放到柜子,然后给你发送取件短信即可,这样以来效率大大提高,这正体现了中间件的好处;

  4. 削峰;当双十一来临后,快递小哥的业务量倍增,这样快递小哥直接怀疑人生,自从各种丰巢、菜鸟驿站等各种消息中间件后,快递小哥没那么繁忙了,这正是体现了消息中间件的削峰作用。

5.23 Kafka数据丢失问题解决方案?

首先对kafka进行限速, 其次启用重试机制,重试间隔时间设置长一些,最后Kafka设置 acks=all,即需要相应的所有处于ISR的分区都确认收到该消息后,才算发送成功。

5.24 Kafka数据重复问题解决方案?

把kafka消费者的配置 enable.auto.commit设为false禁止kafka自动提交offset,从而使用spring-kafka提供的offset提交策略。spring-kafka中的offset提交策略可以保证一批消息数据没有完成消费的情况下,也能提交offset,从而避免了提交失败而导致永远重复消费的问题。

5.24 kafka消息是否会丢失?为什么?

acks参数用于控制producer生产消息的持久性(durability)。对producer而言,kafka在乎的是已提交消息的持久性。一旦消息被提交成功,那么只要有任何一个保存了消息的副本存活,这条消息被视为不会丢失的。   acks有3个值:0,1,all(-1)

  • acks=0:producer不进行消息接收是否成功的确认。
  • acks=1(默认):当Leader副本接收成功后,返回接收成功确认信息;
  • acks=all或者-1:当Leader和Follower副本都接收成功后,返回接收成功确认信息。

  从上面acks的设置中可以知道,当acks=0时,一旦发生宕机,肯定会发生消息丢失的情况;acks=1时,只要leader broker一直存活,kafka能够保证消息不丢失;acks=all时,只要还有副本存活,那么这条消息肯定不会丢失。