架构设计面试系列-01

2022年7月17日
大约 19 分钟

架构设计面试系列-01

1. 软件架构设计都有哪些基本原则?

1、开闭原则(OCP Open Close Principle)

Software entities should be open for extension, but closed for modification.

定义:软件中的对象(类、模块、函数等)应该对于扩展是开放的,但是对于修改是封闭的。简单的说就是程序中类应该是易于扩展的,而不是一有什么新的需求或者变化就去修改原来的代码,这样很容易出现问题。

2、里氏替换原则(LSP Liskov Substitution Principle)

定义:在基类出现的地方,其子类也可以出现,并且不会出现错误。就是利用Java面向对象思想中的继承、多态的特性。简单来说凡是基类适用的地方,子类一定适用。

3、依赖倒转原则(DIP Dependence Inversion Principle)

定义:高层次的模块不依赖于低层次模块的实现细节。简单来说就是依赖抽象(高层次模块)而不依赖具体实现(低层次模块)。

4、迪米特原则(最少知识原则 LKP/LOD Least Knowledge Principle)

定义:一个类应该对自己需要耦合或者调用的类知道的最少,类的内部如何实现与调用者或者依赖者没关系,调用者或者依赖者只需要知道它需要的方法即可。

5、接口隔离原则(ISP InterfaceSegregation Principles)

定义:不依赖不需要的接口,从而容易重构、更改和重新部署。

6、单一职责原则(SRP Single Responsibility Principle)

定义:对一个类来说,应该仅有一个引起它变化的原因。简单来说就是模块的划分需要做到职责清晰,不要既能满足A需求,又能满足B需求,这样耦合太强,不利于后期的扩展和维护。

2. 什么是前后端分离架构?

前后端分离已成为互联网项目开发的业界标准使用方式,例如通过nginx与tomcat的方式(也可以中间加一个nodejs)有效的进行解耦,并且前后端分离会为以后的大型分布式架构、弹性计算架构、微服务架构、多端化服务(多种客户端,例如:浏览器,车载终端,安卓,IOS等等)打下坚实的基础。这个步骤是系统架构从“猿”进化成“人”的必经之路。

核心思想: 前端HTML页面通过AJAX调用后端的RESTFUL API接口并使用JSON数据进行交互。

Web服务器: 一般指像Nginx,Apache这类的服务器,他们一般只能解析静态资源;

应用服务器: 一般指像Tomcat,Jetty,Resin这类的服务器可以解析动态资源也可以解析静态资源,但解析静态资源的能力没有web服务器好;

一般都是只有web服务器才能被外网访问,应用服务器只能内网访问。

以前的Java Web项目大多数都是Java程序员既搞前端,又搞后端。随着技术的发展,渐渐的许多大中小公司开始把前后端的界限分的越来越明确,前端工程师只管前端的事情,后端工程师只管后端的事情。正所谓术业有专攻,一个人如果什么都会,那么他毕竟什么都不精。大中型公司需要专业人才,小公司需要全才。

3. 什么是三层架构?

三层架构就是为了符合“高内聚,低耦合”思想,把各个功能模块划分为表示层(UI)、业务逻辑层(BLL)和数据访问层(DAL)三层架构,各层之间采用接口相互访问,并通过对象模型的实体类(Model)作为数据传递的载体,不同的对象模型的实体类一般对应于数据库的不同表,实体类的属性与数据库表的字段名一致。

三层架构区分层次的目的是为了“高内聚,低耦合”。开发人员分工更明确,将精力更专注于应用系统核心业务逻辑的分析、设计和开发,加快项目的进度,提高了开发效率,有利于项目的更新和维护工作。

三层架构主要是指将业务应用规划中的表示层UI、数据访问层DAL以及业务逻辑层BLL,其分层的核心任务是“高内聚低耦合”的实现。在整个软件架构中,分层结构是常见和普通的软件结构框架,同时也具有非常重要的地位和意义。这种三层架构可以在软件开发的过程中,划分技术人员和开发人员的具体开发工作,重视核心业务系统的分析、设计以及开发,提高信息系统开发质量和开发效率,进而为信息系统日后的更新与维护提供很大的方便。

4. 三层架构和 MVC 有什么区别?

MVC(模型Model-视图View-控制器Controller)是一种架构模式,可以用它来创建在域对象和UI表示层对象之间的区分。

同样是架构级别的,相同是在于它们都有一个表现层,但是它们不同是在于其他的两个层。

在三层架构中没有定义Controller的概念。这是最不同的地方。而MVC也没有把业务的逻辑访问看成两个层,这是采用三层架构或MVC搭建程序最主要的区别。在三层中也提到了Model,但是三层架构中Model的概念与MVC中Model的概念是不一样的,“三层”中典型的Model层是由业务逻辑与访问数据组成的。而MVC里,则是以实体类构成的。

MVC架构

  • M即Model(模型层),主要负责处理业务逻辑以及数据库的交互;

  • V即View(视图层),主要负责显示数据和提交数据;

  • C即Controller(控制层),主要是永作辅助捕获请求并控制请求转发。

三层架构

  • UI界面层;

  • BLL业务逻辑层;

  • DAL数据访问层。

5. 描述一下长链接转换成短链接实现方案?

目前比较公认的方案有以下三种:

1)分布式ID生成器产生ID;

2)ID转62进制字符串;

3)记录数据库,根据业务要求确定过期时间,可以保留部分永久链接。

注意的是其难点在于分布式ID生成。鉴于短链一般没有严格递增的需求,可以使用预先分发一个号段,然后生成的方式。

例如:新浪微博的短链接,8位,理论上可以保存超过200万亿对关系。

6. 什么是分库分表?为什么要分库分表?

欢迎大家关注微信公众号: Java精选 ,专注分享前沿资讯,BATJ 大厂面试题解读,架构技术干货,微服务、高可用等架构设计,10年开发老兵帮你少走弯路,欢迎各领域程序员交流学习!

此类面试题只能在微信小程序: Java精选面试题 ,查阅全部内容,感谢支持!

7. 分库分表常用工具都有哪些?

欢迎大家关注微信公众号: Java精选 ,专注分享前沿资讯,BATJ 大厂面试题解读,架构技术干货,微服务、高可用等架构设计,10年开发老兵帮你少走弯路,欢迎各领域程序员交流学习!

此类面试题只能在微信小程序: Java精选面试题 ,查阅全部内容,感谢支持!

8. 分库分表有哪几种实现方式?

垂直分表

垂直分表在日常开发和设计中比较常见,通俗的说法叫做大表拆小表,拆分是基于关系型数据库中的列(字段)进行的。通常情况,某个表中的字段比较多,可以新建立一张扩展表,将不经常使用或者长度较大的字段拆分出去放到扩展表中。

垂直分库

按照业务模块来划分出不同的数据库,而不是像早期一样将所有的数据表都放到同一个数据库中。

水平分表

水平分表也称为横向分表,将表中不同的数据行按照一定规律分布到不同的数据库表中(这些表保存在同一个数据库中),这样来降低单表数据量,优化查询性能。

最常见的方式就是通过主键或者时间等字段进行Hash和取模后拆分。

水平分库分表

水平分库分表与水平分表的思想相同,唯一不同的就是将这些拆分出来的表保存在不同的数据中。

9. 【字节跳动】简述一下分布式 ID 都有哪些生成策略?

欢迎大家关注微信公众号: Java精选 ,专注分享前沿资讯,BATJ 大厂面试题解读,架构技术干货,微服务、高可用等架构设计,10年开发老兵帮你少走弯路,欢迎各领域程序员交流学习!

此类面试题只能在微信小程序: Java精选面试题 ,查阅全部内容,感谢支持!

10. 常见的分布式限流都有哪些方法?

常见的限流方法:

  • 固定窗口计数器:按照时间段划分窗口,有一次请求就+1,最为简单的算法,但这个算法有时会让通过请求量允许为限制的两倍。

  • 滑动窗口计数器:通过将窗口再细分,并且按照时间“滑动”来解决突破限制的问题,但是时间区间的精度越高,算法所需的空间容量就越大。

  • 漏桶:请求类似水滴,先放到桶里,服务的提供方则按照固定的速率从桶里面取出请求并执行。缺陷也很明显,当短时间内有大量的突发请求时,即便此时服务器没有任何负载,每个请求也都得在队列中等待一段时间才能被响应。

  • 令牌桶:往桶里面发放令牌,每个请求过来之后拿走一个令牌,然后只处理有令牌的请求。令牌桶满了则多余的令牌会直接丢弃。令牌桶算法既能够将所有的请求平均分布到时间区间内,又能接受服务器能够承受范围内的突发请求,因此是目前使用较为广泛的一种限流算法。

Google的开源项目guava提供了RateLimiter类,实现了单点的令牌桶限流。分布式环境下,可以考虑用Redis+Lua脚本实现令牌桶。

11. 如何实现分布式定时任务?

任务轮询或任务轮询+抢占排队方法

  • 每个服务器首次启动时加入队列;

  • 每次任务运行首先判断自己是否是当前可运行任务,如果是便运行;

  • 如果不是当前运行的任务,检查自己是否在队列中,如果在,便退出,如果不在队列中,进入队列。

12. 如何实现类似微博数据量级推送机制?

欢迎大家关注微信公众号: Java精选 ,专注分享前沿资讯,BATJ 大厂面试题解读,架构技术干货,微服务、高可用等架构设计,10年开发老兵帮你少走弯路,欢迎各领域程序员交流学习!

此类面试题只能在微信小程序: Java精选面试题 ,查阅全部内容,感谢支持!

13. 如何实现超出内存限制的大文件排序?

外归并排序:

  • 对文件分割,然后分别排序;

  • 排好序的文件依次读取一个缓冲区的大小,然后进行排序,输出到输出缓冲区,然后保存到结果文件。

如果是数字,可以用位图排序,但是要求比较苛刻:

  • 数字不重复;

  • 知道最大值;

  • 相对密集,因为没出现的数字也会占用空间。

比较适合电话号之类的。

14. 假设一个外卖单要发布,现有200名起手都想接这一单,如何保证只有一个骑手接到此单?

问题分析: 200名骑手抢完这个外卖单的话,应该可以继续参与后续外卖订单的抢单任务,之后抢到订单后,才能进入到下一梯队。

使用redis的lpush命令插入200名骑手的key值,之后使用rpop命令删除第一名插入的key值并返回其删除的key值,来实现只有一个骑手接到此单。

lpush

添加一个或多个元素插入到list的头部

lpush key [values...]

实例

127.0.0.1:6379> lpush mylist java c++ c
(integer) 3
127.0.0.1:6379> lrange mylist 0 -1
1) "公众号"
2) "Java精选"
3) "欢迎关注"

rpop

从list的尾部删除元素,并返回删除元素

rpop key

实例

127.0.0.1:6379> rpop mylist1
"公众号"

15. 美团首页每日从10000个商家中推荐50个商家置顶,每个商家有一个权重,如何推荐?第二日如何更新推荐的商家?

可以使用堆排,第二天更新推荐的可以在要更新商家之前的时候,在redis做计算操作,然后放数据库做个同步即可。

16. 什么是中台?

首先,从字面理解“中台”这个“中”不管是空间概念还是架构体系都跟中没有任何关系,这样就能少了很多困扰。

在中台概念提出前,开发习惯讲产品区分为“前台”、“后台”两个组成,核心区别在于面向不同操作对象。

前台:前台指的是与用户直接交换的产品功能界面,如用户操作的web页面、移动端APP等,包含反应面向独立用户操作测查询报表、订单等。

后台:指的是企业管理员、运营人员进行配置的功能界面,如用户管理、商品管理、物流、结算等,为前台实现功能配置。

两者之间简单的关系如下:

image.png

从图中可以看出,即使是不同的产品,前台的搜索、支付模块及后台的用户管理、财务中心模块都是相同的功能,那么每套产品我们都要对相同功能进行开发,占用了过多开发资源。同时,作为企业整体管理,还需要将用户、现金流等数据进行互通、并统一管理,增加了接口开发工作量的同时,每增加一款新的产品,都会投入大量的开发成本与原有产品体系集成。

为了提升效率,降低产品体系的耦合度,就需要整合出一个中间组织,为不同独立产品线提供公共资源。这个共用的中间组织,就是开发中常说的“中台”。

image.png

这是基于产品维度对中台的解释,对于技术人员或管理人员站在其他维度来说,对于中台的理解会有不同,但特点是是一致的。

打造中台的企业越来越多,包括腾讯、百度、京东、美团、滴滴等企业,=都在中台赛道上各显其能,希望借助于中台成功实现数字化升级。

17. 如何防止 HA 集群的脑裂?

脑裂(split-brain),指在一个高可用(HA)系统中,当联系着的两个节点断开联系时,本来为一个整体的系统,分裂为两个独立节点,这时两个节点开始争抢共享资源,结果会导致系统混乱,数据损坏。

对于无状态服务的HA,无所谓脑裂不脑裂;但对有状态服务(比如MySQL)的HA,必须要严格防止脑裂。(但有些生产环境下的系统按照无状态服务HA的那一套去配置有状态服务,结果可想而知…)

防止HA集群脑裂,一般采用2个方法。

1、仲裁

当两个节点出现分歧时,由第3方的仲裁者决定听谁的。这个仲裁者,可能是一个锁服务,一个共享盘或者其它什么东西。

2、fencing

当不能确定某个节点的状态时,通过fencing把对方干掉,确保共享资源被完全释放,前提是必须要有可靠的fence设备。

理想的情况下,以上两者一个都不能少。但是,如果节点没有使用共享资源,比如基于主从复制的数据库HA,我们也可以安全的省掉fence设备,只保留仲裁。而且很多时候我们的环境里也没有可用的fence设备,比如在云主机里。所以,单纯的双节点,无论如何也防止不了脑裂。

18. 数据库中主从切换后数据能否保证不丢?

主从切换后数据会不会丢和脑裂可以认为是2个不同的问题。还以PostgreSQL或MySQL的数据复制为例来说明。

对PostgreSQL,如果配置成同步流复制,可以做到不管路由是否正确,都不会丢数据。因为路由到错误节点的客户端根本写不进任何数据,它会一直等待从节点的反馈,而它以为的从节点,现在已经是主子了,当然不会理它。当然如果老是这样也不好,但它给集群监视软件纠正路由错误提供了充足的时间。

如果本来就是配置的异步复制,那就是说已经做好丢数据的准备了。这时候,主从切换时丢点数据也没啥大不了,但要控制自动切换的次数。比如控制已经被failover掉的原主不允许自动上线,否则如果因为网络抖动导致故障切换,那么主从就会不停的来回切,不停的丢数据,破坏数据的一致性。

19. 微服务架构主要适用于解决哪些问题?

1、难以扩展

一体化架构应用只能通过在负载均衡器后面放置整个应用程序的多个实例来进行水平扩展。如果应用中的特定服务需要扩展,则没有简单的选项。我们需要完整地扩展应用程序,这显然会造成不必要的资源浪费。

相比之下,基于微服务的应用程序允许我们根据需要独立扩展单个服务。在上图中,如果需要缩放服务B,则可以有10个实例,同时保持其他实例,并可以根据需要随时更改。

2、交付时间长

一体化架构在单个应用的任何部分/层中进行的任何更改都需要构建和部署整个应用程序。个人开发人员还需要下载整个应用程序代码来修复和测试,而不仅仅是受影响的模块,这就影响到了持续部署的效率。

而在微服务架构中,如果仅在一百个微服务中的一个中需要改变,则仅构建和部署改变的微服务,没有必要部署一切。我们甚至可以在短时间内多次部署。

3、应用复杂性

过去,随着应用规模的增长(功能、功能等),团队也会相应扩张,应用很快就就会变得复杂和交织在一起。随着不同的团队不断修改代码,维护模块化结构慢慢变得越来越困难,并慢慢导致像意大利面一样交织的代码。这不仅会影响代码质量,还会影响整个组织。

在基于微服务的应用中,每个团队都在单独的微服务上工作,代码会有序很多。

4、没有明确的所有权

在一体化应用中,看起来独立的团队实际上并不是独立的。它们同时在相同的代码库上工作,严重依赖于彼此。

在基于微服务的应用中,独立团队处理单独的微服务。一个团队将拥有一个完整的微服务。工作的明确所有权明确控制服务的一切,包括开发、部署和监控。

5、故障级联

如果没有正确设计,一体化交媾应用的一部分失败可能会级联并导致整个系统崩溃。

在基于微服务的架构的情况下,我们可以使用断路器来避免这种故障。

6、Dev和Ops之间的墙

开发团队通常会进行开发、测试,一旦部署,就会将维护和支持的所有权交给运维团队,应用此时与开发团队无关了,而运维团队需要努力在生产环境中支持一体化架构应用。

在基于微服务的应用中,团队的组织理解为“构建它、运行它”,开发团队继续在生产中拥有该应用。

7、陷入某种技术/语言

使用一体化架构,意味着被某种已实现的技术/语言锁定。如果需要更改技术/语言,则必须重写整个应用程序。

使用微服务,每个服务可以根据需求和业务以不同的技术或语言实现。任何改变服务技术/语言的决定都只需要重写该特定服务,因为所有微服务都是相互独立的。

8、支持微服务的正确工具/技术的可用性

之前对于开发者来说没有适当的工具和技术来支持微服务。但自从Docker容器和云基础设施(特别是PaaS)向大众提供服务以来,微服务正在大规模采用,因为它们提供了我们所需的“自由”,而无需进行传统的配置程序。