Java后端面试题 - 架构篇

2030次阅读  |  发布于4年以前

id全局唯一且自增,如何实现?

★如何设计算法压缩一段URL?

通过发号策略,给每一个过来的长地址,发一个号即可,小型系统直接用mysql的自增索引就搞定了。如果是大型应用,可以考虑各种分布式key-value系统做发号器。不停的自增就行了。第一个使用这个服务的人得到的短地址是http://xx.xx/0 第二个是 http://xx.xx/1 第11个是 http://xx.xx/a 第依次往后,相当于实现了一个62进制的自增字段即可。

常用的url压缩算法是短地址映射法。具体步骤是:

  1. 将长网址用md5算法生成32位签名串,分为4段,,每段8个字符;
  2. 对这4段循环处理,取每段的8个字符, 将他看成16进制字符串与0x3fffffff(30位1)的位与操作,超过30位的忽略处理;
  3. 将每段得到的这30位又分成6段,每5位的数字作为字母表的索引取得特定字符,依次进行获得6位字符串;
  4. 这样一个md5字符串可以获得4个6位串,取里面的任意一个就可作为这个长url的短url地址。

★Dubbo负载均衡策略?

随机、轮询、最少使用、一致性哈希(除了一致性哈希外,都有加权)

负载均衡算法?

Dubbo中Zookeeper做注册中心,如果注册中心集群都挂掉,发布者和订阅者之间还能通信么?

可以,因为dubbo在注册中心挂掉之后,会从原先的缓存中读取连接地址。

★Dubbo完整的一次调用链路介绍?

调用方:

  1. 将方法名方法参数传入InvokerInvocationHandler的invoke方法中,对于Object中的方法toString, hashCode, equals直接调用invoker的对应方法。

  2. 然后进入(故障转移集群)MockClusterInvoker.invoke()方法中。三种调用策略:①不需要mock, 直接调用FailoverClusterInvoker。②强制mock,调用mock。③先调FailoverClusterInvoker,调用失败在mock.

  3. FailoverClusterInvoker默认调用策略。①通过目录服务查找到所有订阅的服务提供者的Invoker对象。②路由服务根据策略(比如:容错策略)来过滤选择调用的Invokers。③通过负载均衡策略LoadBalance来选择一个Invoker

  4. 执行选择的Invoker.invoker(invocation),经过监听器链,经过过滤器链,执行到远程调用的DubboInvoker。

  5. DubboInvoker根据url 也就是根据服务提供者的长连接,这里封装成交互层对象ExchangeClient供这里调用,判断远程调用类型同步,异步还是oneway模式。ExchangeClient发起远程调用。

6.获取调用结果:①Oneway返回空RpcResult②异步,直接返回空RpcResult, ResponseFuture回调③同步, ResponseFuture模式同步转异步,等待响应返回

消费方:

  1. 通过Invocation获取服务名和端口组成serviceKey=com.alibaba.dubbo.demo.DemoService:20880, 从DubboProtocol的exproterMap中获取暴露服务的DubboExporter, 在从dubboExporter 获取invoker返回

  2. 经过过滤器链。

  3. 经过监听器链。

  4. 到达执行真正调用的invoker, 这个invoker由代理工厂ProxyFactory.getInvoker(demoService, DemoService.class, registryUrl)创建,具体请看代理那部分介绍。

  5. 调用demoService实例方法,将结果封装成RpcResult返回。

★SpringCloud和Dubbo有什么不一样?

1.dubbo采用RPC的方式交互,SpringCloud采用Http,restful协议进行交互。

2.dubbo依赖zookeeper进行服务注册,Springloud自己拥有自己的服务注册中心。

3.dubbo需要强依赖,需要持有相同的类或者jar包,springcloud弱依赖,但需要通过接口文档进行约束。

4.C数据一致性,A服务可用性,P服务对网络分区故障的容错性,Zookeeper 保证的是CP,euraka保证的是AP。

使用Redis如何实现分布式锁?

setnx指令,设置锁的有效时间防止死锁。设置一个随机值来标识锁的持有人,利用这个随机值来释放锁。

Tomcat如何优化?

  1. server模式。

  2. 最大堆最小堆大小。

  3. 年轻代和老年代的比例。

  4. 开启优化。

  5. 使用偏向锁。

  6. gc年龄。

  7. 合适的gc

  1. maxThread。

  2. minThread。

  3. acceptCount。

  4. connectionTimeout。

  5. maxProcessors与minProcessors。

★幂等的处理方式?

  1. 查询与删除操作是天然幂等

  2. 唯一索引,防止新增脏数据

  3. token机制,防止页面重复提交

  4. 悲观锁 for update

  5. 乐观锁(通过版本号/时间戳实现, 通过条件限制where avai_amount-#subAmount# >= 0)

  6. 分布式锁

  7. 状态机幂等(如果状态机已经处于下一个状态,这时候来了一个上一个状态的变更,理论上是不能够变更的,这样的话,保证了有限状态机的幂等。)

  8. select + insert(并发不高的后台系统,或者一些任务JOB,为了支持幂等,支持重复执行)

  9. redis存储订单号,如果已存在的话说明已经处理过

后台系统怎么防止用户恶意频繁访问?

设计一个数据结构,有用户id,当前秒数,调用次数。每次请求时对比当前秒数和该对象内的是否一致,一致的话累加调用次数。不一致的话,将当前秒数替换成新的,调用次数清0。

★请谈谈单点登录原理?

★谈谈项目中分布式事务应用场景?

  1. 学勤系统与账户余额系统

  2. 招生系统与账户余额系统

★MQ和数据库的一致性问题,MQ消息最终一致性。

  1. 事务消息与普通消息的区别就在于消息生产环节,生产者首先预发送一条消息到MQ(这也被称为发送half消息)

  2. MQ接受到消息后,先进行持久化,则存储中会新增一条状态为待发送的消息

  3. 然后返回ACK给消息生产者,此时MQ不会触发消息推送事件

  4. 生产者预发送消息成功后,执行本地事务

  5. 执行本地事务,执行完成后,发送执行结果给MQ

  6. MQ会根据结果删除或者更新消息状态为可发送

  7. 如果消息状态更新为可发送,则MQ会push消息给消费者,后面消息的消费和普通消息是一样的

★正在处理的队列突然断电怎么办?

★服务限流的方式

RabbitMQ消息堆积怎么处理?

新建一个topic,partition是原来的10倍;然后写一个临时的分发数据的consumer程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的10倍数量的queue;接着临时征用10倍的机器来部署consumer,每一批consumer消费一个临时queue的数据;等快速消费完积压数据之后,得恢复原先部署架构,重新用原先的consumer机器来消费消息;

★kafka消息会不会丢失?

Kafka消息发送分同步(sync)、异步(async)两种方式。默认是使用同步方式,可通过producer.type属性进行配置;Kafka保证消息被安全生产,有三个选项分别是0,1,-1。

网络异常:

acks设置为0时,不和Kafka集群进行消息接受确认,当网络发生异常等情况时,存在消息丢失的可能;

客户端异常:

异步发送时,消息并没有直接发送至Kafka集群,而是在Client端按一定规则缓存并批量发送。在这期间,如果客户端发生死机等情况,都会导致消息的丢失;

缓冲区满了:

异步发送时,Client端缓存的消息超出了缓冲池的大小,也存在消息丢失的可能;

Leader副本异常:

acks设置为1时,Leader副本接收成功,Kafka集群就返回成功确认信息,而Follower副本可能还在同步。这时Leader副本突然出现异常,新Leader副本(原Follower副本)未能和其保持一致,就会出现消息丢失的情况;

以上就是消息丢失的几种情况,在日常应用中,我们需要结合自身的应用场景来选择不同的配置。

想要更高的吞吐量就设置:异步、ack=0;想要不丢失消息数据就选:同步、ack=-1策略

★RabbitMQ的消息丢失解决方案?

★kafka的leader副本选举?

kafka消息的检索?

其实很简单主要是用二分查找算法,比如我们要查找一条offest=10000的文件,kafka首先会在对应分区下的log文件里采用二分查看定位到某个记录该offest

=10000这条消息的log,然后从相应的index文件定位其偏移量,然后拿着偏移量到log里面直接获取。这样就完成了一个消息的检索过程。

★RabbitMQ 集群方式?

  1. 普通集群:
  1. 镜像集群:

★kafka高性能的原因?

  1. Broker NIO异步消息处理,实现了IO线程与业务线程分离;

  2. 磁盘顺序写;

  3. 零拷贝(跳过用户缓冲区的拷贝,建立一个磁盘空间和内存的直接映射,数据不再复制到用户态缓冲区);

  4. 分区/分段(每次文件操作都是对一个小文件的操作,非常轻便,同时也增加了并行处理能力);

  5. 批量发送 (可以指定缓存的消息达到某个量的时候就发出去,或者缓存了固定的时间后就发送出去,大大减少服务端的I/O次数)

  6. 数据压缩

★ZooKeeper分布式高可用

★如何设计秒杀

  1. 对于大促时候的秒杀活动,一般运营会配置静态的活动页面,配置静态活动页面主要有两个目的一方面是为了便于在各种社交媒体分发,另一方面是因为秒杀活动页的流量是大促期间最大的,通过配置成静态页面可以将页面发布在公有云上动态的横向扩展;

  2. 将秒杀活动的静态页面提前刷新到CDN节点,通过CDN节点的页面缓存来缓解访问压力和公司网络带宽,CDN上缓存js、css和图片;

  3. 将活动H5页面部署在公有云的web server上,使用公有云最大的好处就是能够根据活动的火爆程度动态扩容而且成本较低,同时将访问压力隔离在公司系统外部;

  4. 在提供真正商品秒杀业务功能的app server上,需要进行交易限流、熔断控制,防止因为秒杀交易影响到其他正常服务的提供,我们在限流和熔断方面使用了hystrix,在核心交易的controller层通过hystrix进行交易并发限流控制,当交易流量超出我们设定的限流最大值时,会对新交易进行熔断处理固定返回静态失败报文。

  5. 服务降级处理,除了上面讲到的限流和熔断控制,我们还设定了降级开关,对于首页、购物车、订单查询、大数据等功能都会进行一定程度的服务降级,例如我们会对首页原先动态生成的大数据页面布局降级为所有人看到的是一样的页面、购物车也会降级为不在一级页面的tabbar上的购物车图标上显示商品数量、历史订单的查询也会提供时间周期较短的查询、大数据商品推荐也会提供一样的商品推荐,通过这样的降级处理能够很好的保证各个系统在大促期间能够正常的提供最基本的服务,保证用户能够正常下单完成付款。

  6. 上面介绍的都是如何保证能扛住高并发,下面介绍下整个方案中如何防止超卖现象的发生,我们日常的下单过程中防止超卖一般是通过在数据库上实施乐观锁来完成,使用乐观锁虽然比for update这种悲观锁方式性能要好很多,但是还是无法满足秒杀的上万并发需求,我们的方案其实也很简单实时库存的扣减在缓存中进行,异步扣减数据库中的库存,保证缓存中和数据库中库存的最终一致性。

高性能统计UV的方式?

(1)使用redis的set集合

(2)使用redis的bitmap(注意内存消耗)

★缓存击穿的解决办法

  1. 加载DB时同步,其他则等待;DB端做SQL合并,Queue合并排队处理;

  2. 部分缓存设置为永不过期;

  3. 读取数据时候则等待500ms,500ms缓存应该已经加载完成;

★后台系统怎么防止请求重复提交。

前端js,控制按钮。前端放置令牌。

数据库唯一索引。redis看key是否存在。或者数据库字段状态。

有没有遇到进线上GC,出现的症状是什么样的,怎么解决的?

利用堆快照,查看到底是哪些对象占用大量内存导致经常gc

★假如你的项目出现性能瓶颈了,你觉得可能会是哪些方面,怎么解决问题。

DB层面,有可能是sql,索引,表过大,数据库压力。

缓存层面:有可能缓存命中率差,redis性能瓶颈,需要扩容

服务器压力:服务器处理瓶颈

Java层面:代码写法

前端层面:cdn压力,页面压力

情景题:如果一个外卖配送单子要发布,现在有200个骑手都想要接这一单,如何保证只有一个骑手接到单子?

分布式锁,或者幂等接口,CAS乐观锁

场景题:美团首页每天会从10000个商家里面推荐50个商家置顶,每个商家有一个权值,你如何来推荐?第二天怎么更新推荐的商家?

可以借鉴下stackoverflow,视频网站等等的推荐算法。

场景题:微信抢红包问题

悲观锁,乐观锁,存储过程放在mysql数据库中。

场景题:1000个任务,分给10个人做,你怎么分配,先在纸上写个最简单的版本,然后优化。

全局队列,把1000任务放在一个队列里面,然后每个人都是取,完成任务。

分为10个队列,每个人分别到自己对应的队列中去取务。

分布式服务如何跟踪?

调用可以实现跟踪系统,可以在业务日志中添加调用链ID,各个环节RPC均添加调用时延,QPS等。

非业务组件应该少加入业务代码,服务调用采用买点,也会采用配置采样率方式,买点即当前节点的上下文信息,包含TraceId,RPCId,开始结束时间,类型,协议,调用方IP,端口,服务名等,以及其他异常信息,报文等扩展,日志采用离线+实时的如flume结合kafka等,应按照TraceId汇总日志后按RPCId顺序整理。

Sentinel 工作原理?

  1. 每个 Sentinel 以每秒钟一次的频率向它所知的 Master,Slave 以及其他 Sentinel 实例发送一个 PING 命令;

2 如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被 Sentinel 标记为主观下线;

  1. 如果一个 Master 被标记为主观下线,则正在监视这个 Master 的所有 Sentinel 要以每秒一次的频率确认 Master 的确进入了主观下线状态;

  2. 当有足够数量的 Sentinel(大于等于配置文件指定的值)在指定的时间范围内确认 Master 的确进入了主观下线状态,则 Master 会被标记为客观下线;

  3. 在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率向它已知的所有 Master,Slave 发送 INFO 命令;当 Master 被 Sentinel 标记为客观下线时,Sentinel 向下线的 Master 的所有 Slave 发送 INFO 命令的频率会从 10 秒一次改为每秒一次;

  4. 若没有足够数量的 Sentinel 同意 Master 已经下线, Master 的客观下线状态就会被移除;

  5. 若 Master 重新向 Sentinel 的 PING 命令返回有效回复, Master 的主观下线状态就会被移除。

redis的主从?

监控( Monitoring ):Redis Sentinel 实时监控主服务器和从服务器运行状态;

自动故障转移:如果一个 master 不正常运行了,哨兵可以启动一个故障转移进程,将一个 slave 升级成为 master,其他的 slave 被重新配置使用新的 master,并且应用程序使用 Redis 服务端通知的新地址;

讲讲分布式唯一ID。

雪花算法:

★什么是一致性hash

利用哈希环进行一致性哈希

★如何使用redis和zookeeper实现分布式锁?有什么区别优缺点,会有什么问题,分别适用什么场景。(延伸:如果知道redlock,讲讲他的算法实现,争议在哪里)

Redis实现比较复杂,流程如下:

从实现难度上来说,Zookeeper实现非常简单,实现分布式锁的基本逻辑:

区别:

redlock算法实现:

假设有5个完全独立的redis主服务器

  1. 获取当前时间戳

  2. client尝试按照顺序使用相同的key,value获取所有redis服务的锁,在获取锁的过程中的获取时间比锁过期时间短很多,这是为了不要过长时间等待已经关闭的redis服务。并且试着获取下一个redis实例。比如:TTL为5s,设置获取锁最多用1s,所以如果一秒内无法获取锁,就放弃获取这个锁,从而尝试获取下个锁

  3. client通过获取所有能获取的锁后的时间减去第一步的时间,这个时间差要小于TTL时间并且至少有3个redis实例成功获取锁,才算真正的获取锁成功

  4. 如果成功获取锁,则锁的真正有效时间是 TTL减去第三步的时间差 的时间;比如:TTL 是5s,获取所有锁用了2s,则真正锁有效时间为3s(其实应该再减去时钟漂移);

  5. 如果客户端由于某些原因获取锁失败,便会开始解锁所有redis实例;因为可能已经获取了小于3个锁,必须释放,否则影响其他client获取锁


redlock的争议点:(fgc导致的问题)

对于提升效率的场景下,RedLock 太重。

对于对正确性要求极高的场景下,RedLock 并不能保证正确性。

2pc 3pc 的区别,解决了哪些问题。

3pc 将2pc中的一阶段拆为 canCommit和prepareCommit

二阶段提交有几个缺点:

二阶段无法解决的问题:协调者再发出commit消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否被已经提交。

什么是paxos算法, 什么是zab协议。


paxos算法的推导,

如果只有一个人投票的话,那么每个人必须接受第一个提议。

这样又会导致三个人分别投不同的票,形不成大多数。

推导出可以选多次票,但多次票有可能投自己又投别人,形成多个大多数,所以又加了限制条件,只能同意之前同意过的。

再推导出当两个机子重连的话,机子必须接受第一个发给他的提案,这样就违背了之前选好的。

所以当服务器重连的时候,必须发给他之前同意好的提案。

https://blog.51cto.com/12615191/2086264

zab协议:

原子广播:

崩溃恢复:

★Dubbo的原理,有看过源码么,数据怎么流转的,怎么实现集群,负载均衡,服务注册和发现,重试转发,快速失败的策略是怎样的 。

★一次RPC请求的流程是什么。

  1. 服务消费方(client)调用以本地调用方式调用服务;

  2. client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;

  3. client stub找到服务地址,并将消息发送到服务端;

  4. server stub收到消息后进行解码;

  5. server stub根据解码结果调用本地的服务;

  6. 本地服务执行并将结果返回给server stub;

  7. server stub将返回结果打包成消息并发送至消费方;

  8. client stub接收到消息,并进行解码;

  9. 服务消费方得到最终结果。

★解释什么是MESI协议(缓存一致性)。

MESI协议保证了每个缓存中使用的共享变量的副本是一致的。它核心的思想是:当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取。

(另外一种硬件层面的解决是总线锁)

Zookeeper的用途,选举的原理是什么,适用场景。

用途:类似文件系统的分布式协调服务。

选举原理:

  1. Zookeeper集群中只有超过半数以上的服务器启动,集群才能正常工作;

  2. 在集群正常工作之前,myid小的服务器给myid大的服务器投票,直到集群正常工作,选出Leader;

  3. 选出Leader之后,之前的服务器状态由Looking改变为Following,以后的服务器都是Follower。

适用场景:

  1. 命名服务

  2. 配置管理

  3. 集群管理

  4. 分布式锁

  5. 队列管理

Zookeeper watch机制原理。

  1. 客户端注册Watcher到服务端;

  2. 服务端发生数据变更;

  3. 服务端通知客户端数据变更;

  4. 客户端回调Watcher处理变更应对逻辑;

什么叫数据一致性,你怎么理解数据一致性。

请思考一个方案,实现分布式环境下的countDownLatch。

zookeeper,判断某个节点下的子节点到达一定数目后,则执行,否则等待。

★用过哪些MQ,怎么用的,和其他mq比较有什么优缺点,MQ的连接是线程安全的吗

MQ系统的数据如何保证不丢失

发送消息后和接收消息后 确认机制 加上持久化

MQ有可能发生重复消费,如何避免,如何做到幂等。

唯一主键,或者使用redis做id,

异步模式的用途和意义。

避免阻塞

使用kafka有没有遇到什么问题,怎么解决的。

https://www.cnblogs.com/leaves1024/p/11073191.html

https://blog.csdn.net/chizizhixin/article/details/78563595

https://blog.csdn.net/lsh2366254/article/details/84910011

如何保证消息的有序性。消息处理的有序性。

使用同一个queue

消息的重发补发策略

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8