Java面试题(下)

1.MQ消息可靠性问题

可能导致消息丢失的情况:

  • 发送时丢失

    生产者发送消息未送达交换机

    消息到达交换机后未到达队列

  • MQ宕机,队列中的消息会丢失

  • 消费者接收到消息后未消费就宕机了

解决方案:

  • 生产者确认机制:当消息发送到MQ后,系统会返回一个结果给消息的发送者,说明消息的处理情况,处理结果有两种可能的值

    publisher-confirm,发送者确认

    • 消息成功投递交交换机,系统返回ack(确认)
    • 消息未成功投递到交换机,系统返回nack(未确认)

    publisher-return,发送者回执

    • 消息成功投递到交换机,但是没有成功路由到队列,系统返回ack,同时提供路由失败的原因
  • 对交换机、队列和消息进行持久化

  • 消费者确认消息:当消费者处理消息后可以向MQ发送ack回执,当MQ收到ack回执后才删除该消息(三种确认模式)

    manual:手动ack,调用Spring AMQP提供的API发送ack,存在代码侵入问题

    auto:基于AOP自动发送ack,由Spring检测listener代码是否出现异常,没有异常返回ack,有异常返回nack

    none:关闭ack,MQ假定消息消费者获取消息后会成功处理,因此消息投递后立即被删除

  • 消息消费失败的重试机制:当消费者出现异常后,消息会不断requeue重新入队到队列,再重新发送给消费者,然后再次异常,再次重新入队,无限循环,导致MQ的消息处理的压力大大提高,给MQ服务器带来不必要的压力

    对失败消息的处理策略

    在开启重试模式之后,重试次数耗尽,如果消息依然失败就要执行处理策略,MessageRecoverer接口提供了三种不同的实现

    • 重试次数耗尽后,直接reject丢弃消息(默认方式)
    • 重试次数耗尽后,返回nack,消息重新入队
    • 重试耗尽之后,将失败消息投递到指定的交换机
2.es中拼音搜索如何实现?

拼音搜索要使用到拼音分词器,在分词时自定义分词器使中文分词与文档id建立关系,拼音分词也与文档id建立关系,实现中文的分词和拼音的分词,搜索时使用ik分词器,搜索时不使用自定义分词器为了防止词语的拼音一样,中文含义不一样

3.es中的自动补全如何实现?

es提供了Completion Suggester来实现自动补全,参与补全的字段类型必须是completion类型,字段的内容一般是用来补全的多个词条形成的数组

4.MySQL中的数据和ES中数据怎么同步?

使用MQ异步写,管理端微服务操作数据库,一旦数据库发生更改向MQ中发送消息,用户端微服务监听MQ中的消息同步数据到ES中

如果在同步数据的过程中MQ宕机呢?

使用定时任务,每隔一段时间查询MySQL中今天的数据和ES中今天的数据(不查所有),查询数据的id,将MySQL中的数据id存入Set集合中,ES中的数据id存入Set集合中,将两者做差集。存储MySQL中数据id的集合和存储ES中数据id的集合做差集,MySQL中有ES中没有,同步数据到es中;存储ES中数据id的集合和存储MySQL中数据id的集合做差集,ES中MySQL中没有,删除es中的数据

5.RabbitMQ中交换机的类型

交换机的类型有四种,常见的是三种:广播(发布订阅)(Fanout)、路由(Direct)和通配符(Topic)、Headers

广播:将消息路由到所有绑定交换机的队列

定向:队列与交换机绑定时必须指定一个路由key,消息也是通过交换机和指定的路由key路由给指定的队列

通配符:交换机可以让队列在绑定路由key时使用通配符,匹配一个或多个(#),恰好一个词(*)

6.哨兵机制

哨兵模式实现了高可用,哨兵节点是特殊的redis服务,主要用来监控redis实例节点。

哨兵有三个主要任务:

监控:哨兵会不断的监控主节点和从节点是否正常运作

通知:哨兵充当redis客户端服务发现,当集群发生故障转移时会将最近的信息推送给redis的客户端

自动故障转移:当一个Master(主节点)不能正常工作时,哨兵会进行自动故障转移的操作将失效的Master的其中一个Slave(从节点)升级为新的Master,并让失效的Mater的其他Slave改为复制新的Master。当客户端试图连接失效的Master时,集群也会向客户端返回新的Master地址,让集群可以使用新的Master代替失效的Master,从而实现高可用

集群监控原理:

哨兵基于心跳机制检测服务状态,每隔1秒向集群的每个实例发送ping命令,如果Sentinel哨兵节点发现某实例未在规定时间响应,则认为该实例是主观下线;若超过指定数量(Sentinel实例数量的一半)的Sentinel都认为该实例主观下线,则该实例客观下线

7.JDK动态代理与 cglib动态代理区别

实现原理:

JDK动态代理:

代理对象和目标对象(需要被代理的目标类)要实现同样的接口InvocationHandler。代理对象会在内存生成代理方法,执行代理方法其本质是执行InvocationHandler实现类自己创建的调用处理器的invoke方法,在invoke方法中对被代理类进行功能增强并通过反射调用被代理的同名方法

cglib动态代理:

通过继承被代理的目标类实现代理,被代理类作为父类,动态生成被代理类的子类,子类重写被代理类的所有不是final修饰的方法,代理对象在内存中生成代理的方法都会去调用MethodInterceptor接口的实现类中重写的intercept()方法,这个方法可以实现对被代理方法功能的增强

对比:

JDK动态代理是面向接口的,cglib动态代理是通过字节码底层继承被代理类来实现,由于是创建多个.class文件所以在创建代理时没有JDK快,但是运行速度比JDK动态代理快,因为JDK使用的是反射而cglib是直接调用

8.Spring事务的实现方式和隔离级别

实现方式:编程式和声明式

  • 编程式事务

  • 基于TransactionProxyFactoryBean的声明式事务管理(基于xml)

  • 基于@Transactional的声明式事务管理(基于注解)

    在一个方法上添加了@Transactional注解后Spring会基于这个类生成一个代理对象,会将这个代理对象作为bean,当在使用这个代理对象的方法时如果这个方法上存在@Transactional注解,那么代理逻辑会先把事务的自动提交设置为false,然后再去执行原本的业务逻辑方法,如果执行业务逻辑方法没有出现异常,那么代理逻辑会将事务自动提交,如果执行业务逻辑方法出现了异常,那么会将事务进行回滚(可以使用注解中的rollbackFor属性配置将哪些异常进行回滚,默认情况下对运行时异常和错误异常进行回滚)

  • 基于Aspectj AOP配置事务

隔离级别:解决多个并发事务调用数据库的问题

  • read uncommitted(读未提交)

    允许脏读、不可重复读和幻读。一个事务可以读取另一个事务未提交的数据

  • read commited(读已提交、不可重复读)

    避免脏读。一个事务只能读取已提交的数据,但是可能发生不可重复读和幻读,因为在同一个事务中,另一个事务可能会被修改

  • repeatable read(可重复读)

    避免脏读和不可重复读。在同一个事务中,多次读取同一行数据得到结果是一致的。但是可能发生幻读,因为在同一个事务中另一个事务可能会插入或删除数据

  • serializable(串行化)

    避免脏读、不可重复读和幻读。事务串行执行,保证数据的一致性和完整性

传播机制:解决的是一个事务在多个节点(方法)中传递的问题,事务的传播行为仅在方法调用之间才会生效,对于同一个方法内部的事务性操作传播行为不会起作用

9.CAP定理和BASE理论

分布式系统有三个指标:一致性、可用性、分区容错性,三个指标不可能同时做到就是CAP定理

BASE理论对CAP定理提供了一种解决思路:基本可用(允许损失部分可用性,保证核心可用)、软状态(语允许出现中间状态,临时的不一致状态)、最终一致(在软状态结束后数据达到最终一致)

Seata是一个开源的分布式事务的解决方案,提供了四个模式

  • XA模式:保证强一致性,牺牲了一定的可用性,无代码侵入(配置文件)
  • TCC模式:保证最终一致性,有代码侵入
  • AT模式:保证最终一致性,无代码侵入,也是Seata的默认模式(配置文件)
  • SAGA模式:长事务模式,有代码侵入
10.什么是分布式事务?分布式事务解决方案

分布式事务就是跨数据源、跨服务下产生的事务

解决分布式事务

有两种解决思路,但是不论哪一种解决思路都需要在子系统事务之间互相通讯,协调事务状态,就是事务协调者

  • AP模式:各个子事务分别执行和提交,允许出现结果不一致,然后采用弥补措施恢复数据即可,实现最终一致
  • CP模式:各个子事务执行后相互等待,同时提交,同时回滚,达成强一致。但事务等待过程中处于弱可用状态(可用性低)
11.Redis是单线程还是多线程?

Redis5之前是单线程,Redis6开始引入多线程版本(实际上是单线程+多线程)

使用多线程提升读写解析数据的效率,而操作内存数据的时候还是使用单线程,单线程不会产生线程安全问题,因为Redis在针对内存中的数据操作时是在一个公共的工作队列中实现的,先进先出,之所以使用单线程可以大大降低Redis内部的复杂度

12.Redis持久化机制

RDB持久化

每隔一段时间,一次性将内存中的所有数据存储到磁盘文件中,当Redis实例出现故障重启后,从磁盘读取快照恢复数据。使用bgsave开启子进程执行RDB,避免主进程受到影响

AOF持久化

Redis会把操作以日志的方式记录在appendonly.aof文件中,aof的配置中有三种类型的持久化方式,always执行一次写操作就进行持久化、everysec每秒进行持久化(从缓冲区),no系统默认的刷盘机制进行持久化

文件中存储的是每一条的命令,当重复命令过多时,超过64mb就会触发AOF重写机制,手动触发使用命令bgrewriteof命令

13.MySQL数据库如何实现主从复制?主库出现问题?从库怎么办?

MySQL主从复制主要有三个线程一条master、两条slave(I/O thread、SQL thread),主从复制的基础是主库将数据库所有的变更记录到binlog日志中,从节点I/O线程将binglog日志中的内容写入到relay log文件中,从节点的SQL线程读取relay log文件中的内容对数据进行更改,最终保证主从数据库的一致性

**问题:**由于MySQL默认的复制方式是异步的,主库把日志发送给从库后不关心从库是否已经处理,会产生一个我呢提,如果主库挂了,从库升为主库后日志就丢失了

**解决方案:**全同步复制和半同步复制

  • 全同步复制:主库写入binlog后强制同步日志到从库,所有的从库都执行完成后才返回给客户端写入操作成功,这个方式性能会受到严重影响
  • 半同步复制:从库写入日志成功后返回ACK确认给主库,主库收到至少一个从库的确认就认为写操作完成
14.nacos和eureka的区别

共同点:都支持服务注册和服务拉取;都支持服务提供者心跳方式做健康检测

不同点

  • nacos支持服务端主动检测提供者状态。临时实例采用心跳模式,非临时实例采用主动检测模式(一般情况下都是临时实例)
  • 临时实例心跳不正常会被剔除,非临时实例不会被剔除
  • nacos支持服务列表变更消息推送模式,服务列表更新及时
  • nacos支持AP和CP而eureka只支持AP。nacos集群默认采用AP方式。当集群中存在非临时实例时,采用CP模式;eureka采用AP方式
15.RabbitMQ如何保证消息消费的顺序性?

问题产生:RabbitMQ的一个交换机可以绑定多个队列,生产者在发送消息的时候不同的消息路由到不同的队列,消费者从不同的队列获取消息,导致消息的发送顺序和消费顺序不一致。

解决:消费者在进行消息消费的时候可能采用多线程的方式进行消费,每个线程的处理效率不同,所以无法保证消息的顺序性,同一个队列中的消息也无法保证顺序性。消费者将获取到的消息放入阻塞队列,消息者单独开一个线程从阻塞队列中获取消息

16.消息堆积问题

生产者发送消息的速度超过了消费者处理消息的速度,就会导致队列中消息堆积,知道队列存储消息达到上限,之后发送的消息就会成为死信,可能会被丢弃

解决方案:

  • 增加更多的消费者提高消费速度,也就是工作队列模式
  • 扩大队列容积,提高堆积上限(消息保存在内存中显然不行)
  • 可以采用惰性队列,将接收到的消息直接存入磁盘而非内存,消费者消费消息时才会从磁盘中读取并加载到内存中,也支持数百万条消息的存储。设置队列属性为lazy
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值