大家好,我是一颗甜苞谷,今天分享一下面试中常用的10 个面试点全解析,助你面试中脱颖而出
问题1:微服务架构和传统架构有什么区别,现在市场上的微服务架构有哪些?
答:传统的单体架构可维护性、可读性低,维护成本也会很高,分布式架构就是每个服务独立维护,代码结构清晰,维护成本也会降低,并且服务器压力也会分担给各个独立的服务器各自承担
微服务架构目前就是SpringCloud和dubbo,dubbo作为rpc框架,实现效果就是远程调用做到本地调用一样,提供了两个功能Monitor 监控中心和调用中心,SpringCloud有服务注册、负载均衡、断路器、路由网关和分布式的配置
问题2:介绍一下SpringCloud分布式框架
1、SpringCloud 是一个微服务架构,基于SpringBoot结合,将一个项目分成多个web服务工程,可以支持独立扩展,又可以共同合作,微服务架构更容易维护,更容易阅读和扩展
2、SpringCloud 的主要5个组件,eureka(服务注册中心)、ribbon和feign(负载均衡)、Hystrix(断路器)、Zuul(路由网关)、config(分布式配置)
eureka主要来实现服务注册,服务提供者和服务消费
ribbon和feign主要实现负载均衡,实现各个服务的通讯调用,feign整合了ribbon,整合了Hystrix,具有熔断能力
ribbon是一个基于 HTTP 和 TCP 客户端 的负载均衡的工具。
它可以在客户端配置 RibbonServerList(服务端列表),使用 HttpClient 或 RestTemplate 模拟http请求,步骤相当繁琐
Feign 是在 Ribbon的基础上进行了一次改进,是一个使用起来更加方便的 HTTP 客户端。
采用接口的方式, 只需要创建一个接口,然后在上面添加注解即可 ,将需要调用的其他服务的方法定义成抽象方法即可, 不需要自己构建http请求。
然后就像是调用自身工程的方法调用,而感觉不到是调用远程方法,使得编写 客户端变得非常容易。
feign自身是一个声明式的伪http客户端,写起来更加思路清晰和方便
fegin是一个采用基于接口的注解的编程方式,更加简便
Hystrix是一个链式调用服务,一个延迟和容错库,旨在隔离远程系统,如果其中一个服务宕机,其他的服务可能都会有影响,发生异常,发生雪崩,当一个服务的调用次数达到设置的阈值,断路器就会打开了,避免连锁的故障
Zuul相当于是第三方调用(app应用端和PC端)和服务提供方之间的防护门。作为前端服务(Edge Service也称边缘服务,前端服务的作用是对后端服务做必要的聚合和裁剪后暴露给外部不同的设备,如PC,Pad或者Phone),Zuul旨在实现动态路由,监控,弹性和安全性。它具备根据需求将请求路由到多个AWS自动弹性伸缩组的能力。
问题3:Eureka(AP)比Zookeeper(CP)好在哪里?
答:一个分布式系统不可能同时满足C(一致性)、A(可用性)和P(分区容错性),Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像zookeeper那样使整个注册服务瘫痪
问题4:你了解哪些消息队列?
答:kafka、rocketMQ、RabbitMQ、ActiveMQ
消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构
目前主流的MQ主要是Rocketmq、kafka、Rabbitmq,Rocketmq相比于Rabbitmq、kafka具有主要优势特性有:
1、支持事务型消息(消息发送和DB操作保持两方的最终一致性,rabbitmq和kafka不支持)
2、支持结合rocketmq的多个系统之间数据最终一致性(多方事务,二方事务是前提)
3、支持18个级别的延迟消息(rabbitmq和kafka不支持)
4、支持指定次数和时间间隔的失败消息重发(kafka不支持,rabbitmq需要手动确认)
5、支持consumer端tag过滤,减少不必要的网络传输(rabbitmq和kafka不支持)
6、支持重复消费(rabbitmq不支持,kafka支持)
使用场景:异步处理,应用解耦,流量削锋和消息通讯四个场景
消息丢失问题解决方案:
- 用持久化消息,或者非持久化消息及时处理不要堆积,或者启动事务,启动事务后,commit()方法会负责任的等待服务器的返回,也就不会关闭连接导致消息丢失了。
- 一种是用MQ的事务,但是有个缺点,是阻塞的,影响性能
- 另一种是用confirm模式(优点,就是执行效率高,不需要等待消息执行完,只需要监听消息即可)
问题5:了解非关系型数据库吗?有哪个非关系型数据库,然后讲一下使用的场景
MongoDB
解决数据分析场景需求,用户可以自己写查询语句或脚本,将请求都分发到 MongoDB 上完成
游戏场景,使用 MongoDB 存储游戏用户信息,用户的装备、积分等直接以内嵌文档的形式存储,方便查询、更新
物流场景,使用 MongoDB 存储订单信息,订单状态在运送过程中会不断更新,以 MongoDB 内嵌数组的形式来存储,一次查询就能将订单所有的变更读取出来。
社交场景,使用 MongoDB 存储存储用户信息,以及用户发表的朋友圈信息,通过地理位置索引实现附近的人、地点等功能
物联网场景,使用 MongoDB 存储所有接入的智能设备信息,以及设备汇报的日志信息,并对这些信息进行多维度的分析
视频直播,使用 MongoDB 存储用户信息、礼物信息等
……
使用需求
应用不需要事务及复杂 join 支持
新应用,需求会变,数据模型无法确定,想快速迭代开发
应用需要2000-3000以上的读写QPS(更高也可以)
应用需要TB甚至 PB 级别数据存储
应用发展迅速,需要能快速水平扩展
应用要求存储的数据不丢失
应用需要99.999%高可用
应用需要大量的地理位置查询、文本查询
问题6:让服务器主动给客户端推送消息有几种方式长轮询和长连接
(1)长轮询:
客户端每隔很短的时间,都会对服务器发出请求,查看是否有新的消息,只要轮询速度足够快。;例如1秒,就能造成实时交互的效果。
缺点:对服务器、客户端双方都造成了大量的性能浪费。
(2)长连接:
浏览器和服务器只需要做一个握手的动作,在建立连接之后,双方可以在任意时刻,相互推送信息。同时,服务器与客户端之间交换的头信息很小。
WebSocket 是一种让客户端和服务器之间进行双向实时通信的技术。它是HTML最新标准HTML5的有一个协议规范,本质上是个基于TCP的协议,它通过HTTP/HTTPS协议发送一条特殊的请求进行握手后创建了一个TCP连接。此后浏览器/客户端和服务器之间便可以通过此连接来进行双向实时通信。
问题7:分布式锁简单入门以及三种实现方式介绍
Java中的锁可以简单的理解为多线程情况下访问临界资源的一种线程同步机制。
为了保证一个方法或属性在高并发情况下的同一时间只能被同一个线程执行,在传统单体应用单机部署的情况下,可以使用Java并发处理相关的API(如ReentrantLock或Synchronized)进行互斥控制。在单机环境中,Java中提供了很多并发处理相关的API。但是,随着业务发展的需要,原单体单机部署的系统被演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的Java API并不能提供分布式锁的能力。为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题
需要保证一个方法在同一时间内只能被同一个线程执行。
基于数据库实现分布式锁;
基于缓存(Redis等)实现分布式锁;
基于Zookeeper实现分布式锁;
问题8:高并发解决方案
- 流量优化:防盗链处理。
- 前端优化:减少HTTP请求,合并css或js,添加异步请求,启用浏览器缓存和文件压缩,CDN加速,建立独立图片服务器,页面缓存
- 。
- 服务端优化:页面静态化,并发处理,队列处理,应用和静态资源分离。
- 数据库优化:数据库缓存,分库分表,分区操作,读写分离,负载均衡。
- web服务器优化:集群与分布式,负载均衡,nginx反向代理,7,4层LVS软件。
电商网站中,50W-100W高并发,秒杀功能是怎么实现的
秒杀的特点就是这样时间极短、 瞬间用户量大
那单机的Redis我感觉3-4W的QPS还是能顶得住的,但是再高了就没办法了
大量的请求进来,我们需要考虑的点就很多了,缓存雪崩,缓存击穿,缓存穿透这些我之前提到的点都是有可能发生的,出现问题打挂DB那就很难受了,活动失败用户体验差
遇到问题:
- 超卖
- 恶意请求:黄牛的抢票系统
- 链接暴露:去做url,然后通过前端代码获取url后台校验才能通过
- 数据库:每秒上万甚至十几万的QPS(每秒请求数)直接打到数据库,基本上都要把库打挂掉,而且你服务不单单是做秒杀的还涉及其他的业务,你没做降级、限流、熔断啥的,别的一起挂
设计个能抗住高并发的系统,我觉得还是得单一职责
什么意思呢,大家都知道现在设计都是微服务的设计思想,然后再用分布式的部署方式
也就是我们下单是有个订单服务,用户登录管理等有个用户服务等等,那为啥我们不给秒杀也开个服务,我们把秒杀的代码业务逻辑放一起。
单独给他建立一个数据库,现在的互联网架构部署都是分库的,一样的就是订单服务对应订单库,秒杀我们也给他建立自己的秒杀库。
Redis集群:单机的Redis顶不住嘛,那简单多找几个兄弟啊,秒杀本来就是读多写少,那你们是不是瞬间想起来我之前跟你们提到过的,Redis集群,主从同步、读写分离,我们还搞点哨兵,开启持久化直接无敌高可用
链接暴露解决方案:URL动态化,通过MD5之类的加密算法加密随机的字符串
Nginx:Nginx大家想必都不陌生了吧,这玩意是高性能的web服务器,并发也随便顶几万不是梦,但是我们的Tomcat只能顶几百的并发呀,那简单呀负载均衡嘛,一台服务几百,那就多搞点,在秒杀的时候多租点流量机。恶意请求拦截也需要用到它,一般单个用户请求次数太夸张,不像人为的请求在网关那一层就得拦截掉了,不然请求多了他抢不抢得到是一回事,服务器压力上去了,可能占用网络带宽或者把服务器打崩、缓存击穿等等
Undertow容器技术:Undertow是Red Hat公司的开源产品, 它完全采用Java语言开发,是一款灵活的高性能Web服务器,支持阻塞IO和非阻塞IO。由于Undertow采用Java语言开发,可以直接嵌入到Java项目中使用。同时, Undertow完全支持Servlet和Web Socket,在高并发情况下表现非常出色
资源静态化:秒杀一般都是特定的商品还有页面模板,现在一般都是前后端分离的,所以页面一般都是不会经过后端的,但是前端也要自己的服务器啊,那就把能提前放入cdn服务器的东西都放进去,反正把所有能提升效率的步骤都做一下,减少真正秒杀时候服务器的压力。
限流:
限流这里我觉得应该分为前端限流和后端限流。
前端限流:这个很简单,一般秒杀不会让你一直点的,一般都是点击一下或者两下然后几秒之后才可以继续点击,这也是保护服务器的一种手段。
后端限流:秒杀的时候肯定是涉及到后续的订单生成和支付等操作,但是都只是成功的幸运儿才会走到那一步,那一旦100个产品卖光了,return了一个false,前端直接秒杀结束,然后你后端也关闭后续无效请求的介入了。
Tip:真正的限流还会有限流组件的加入例如:阿里的Sentinel、Hystrix等。我这里就不展开了,就说一下物理的限流。
库存预热:
秒杀的本质,就是对库存的抢夺,每个秒杀的用户来你都去数据库查询库存校验库存,然后扣减库存,撇开性能因数,你不觉得这样好繁琐,对业务开发人员都不友好,而且数据库顶不住啊。
我们要开始秒杀前你通过定时任务或者运维同学提前把商品的库存加载到Redis中去,让整个流程都在Redis里面去做,然后等秒杀介绍了,再异步的去修改库存就好了
但是用了Redis就有一个问题了,我们上面说了我们采用主从,就是我们会去读取库存然后再判断然后有库存才去减库存,正常情况没问题,但是高并发的情况问题就很大了。
这里我就不画图了,我本来想画图的,想了半天我觉得语言可能更好表达一点。
多品几遍!!!就比如现在库存只剩下1个了,我们高并发嘛,4个服务器一起查询了发现都是还有1个,那大家都觉得是自己抢到了,就都去扣库存,那结果就变成了-3,是的只有一个是真的抢到了,别的都是超卖的。咋办?
Lua脚本是类似Redis事务,有一定的原子性,不会被其他命令插队,可以完成一些Redis事务性的操作。这点是关键。
知道原理了,我们就写一个脚本把判断库存扣减库存的操作都写在一个脚本丢给Redis去做,那到0了后面的都Return False了是吧,一个失败了你修改一个开关,直接挡住所有的请求,然后再做后面的事情嘛。
限流&降级&熔断&隔离:
这个为啥要做呢,不怕一万就怕万一,万一你真的顶不住了,限流,顶不住就挡一部分出去但是不能说不行,降级,降级了还是被打挂了,熔断,至少不要影响别的系统,隔离,你本身就独立的,但是你会调用其他的系统嘛,你快不行了你别拖累兄弟们啊。
削峰填谷:
一说到这个名词,很多小伙伴就知道了,对的MQ,你买东西少了你直接100个请求改库我觉得没问题,但是万一秒杀一万个,10万个呢?服务器挂了,程序员又要背锅的。
你可以把它放消息队列,然后一点点消费去改库存就好了嘛,不过单个商品其实一次修改就够了,我这里说的是某个点多个商品一起秒杀的场景,像极了双十一零点。
问题9:如何确保N个线程可以访问N个资源同时又不导致死锁?
使用多线程的时候,一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了
问题10:线程的基本概念、线程的基本状态以及状态之间的关系?
线程指在程序执行过程中,能够执行程序代码的一个执行单位,每个程序至少都有一个线程,也就是程序本身。
Java中的线程有四种状态分别是:运行、就绪、挂起、结束。