点赞再看,养成习惯
理论指导实践,面试指导学习
面试现场
面试官低头快速扫了扫简历,捕捉到了几个关键词:打车app,微服务架构,参与开发微服务,管理几十台服务器,读写QPS数十万。面试官微微一笑,这家伙有点意思,可以交个朋友。
面试开始
面试官心想:友好的面试从试探开始。先问一个青铜级别的问题。问:微服务有什么特点?
最初的软件工程 在软件工程的上一个10年,网站开发的通用规则是一个前端,一个后台,一个数据库。前端后端之间通过 https/http 协议进行数据传输。而后台通常是一台高配主机,搭载 linux/ubuntu 系统,所有业务逻辑代码都被打包成一个整体文件,可以简单理解成一个可执行文件类似 exe,运行在一台主机上。
- 随着业务和代码量的增大,打包一个最终可执行文件的时间越来越长,甚至有可能长达一天。但这与现代软件的敏捷开发,通过快速迭代来进行版本更新的理念冲突。
- 一个机器上的内存总是有限的,一个实习工程师负责一个新功能,不小心打盹写出了个bug,消耗了全部可用内存,导致所有其他功能因缺乏内存而拒绝服务。
![]()
- 于是,在现在互联网诞生了新的设计,微服务。我们将所有代码按照业务逻辑拆分。如电商网站后台可以拆分成5个不同的微服务,用户管理,商品管理,订单管理,促销管理。
- 每个微服务之间可以通过约定的协议或者消息队列通信。由于代码拆分,每个服务的代码量降低,并且可以互不影响得快速迭代。每个微服务运行在不同的机器上,实习工程师完全可以为一个新功能开发一个微服务,再也不怕写bug会被开除了。
于是,我大声背诵早已胸有成竹的答案:
- 可维护性高。每个微服务只需要干好自己的事儿,不需要关注其他微服务的实现。
- 简化部署。快速完成打包集成部署。
- 扩展性强。通过横向扩展,增加机器数量,扛住流量压力。
- 技术易构,易于集成第三方应用系统, 可以简单切换解决方案。
- 可靠性高。核心服务不会受边缘服务的影响。
面试官抬起头看了我一眼,心想:基础很扎实,再来一题。 问:微服务架构一般都有什么组件?
- 微服务之间互相通信无论你是rpc还是http,你都得首先知道对方的ip。所以你需要一个组件保存:<service_name: service_ip>。它叫服务发现。
- 每个微服务刚起来的时候还不知道自己是谁,该连哪个数据库,数据库大门的密码是啥。所以你需要一个高可用的组件保存所有的配置。它叫服务配置中心。
- 现在所有的微服务都知道自己是谁,该怎么联络组织,但你还需要对外提供一个服务网管,对每个连接做鉴权,来保护不对外访问的服务和不受欢迎的黑客。它叫服务网关。
总结一下,微服务有三大基础组件:服务发现,服务配置中心和服务网关。
面试官频频点头,你这娃娃还可以,答到这里,放在几年前已经是不错了,但现在让我们愉快得卷起来。问:如果有上千个微服务,该怎么去做微服务治理?
- 微服务调用链:一个功能需要多个微服务功能完成,称为微服务调用链。因此,对于一般的微服务来说,你会有多个上游,即访问你端口的微服务。会有多个下游,即你调用其他微服务的端口。
- 限流(rate limit): 对上游微服务,上游微服务可能手误,开起几千个线程抄起百万大军准备调用你的服务,所以你需要对它做限流(rate limit),这需要根据业务类型来进行设置,一般限制一秒内上游请求数不能超过几百到几千。
- 熔断(circuit break): 对下游微服务,若响应某段时间内有大量的超时或者错误返回,那预示着它可能快要被巨大的流量整崩溃了,你需要马上停止给下游打流量,让它有恢复的时间。等一段时间再试探一下它,留得青山在,不怕没柴烧嘛,这叫熔断(circuit break),一般限制调用错误率或超时率不超过30%左右。
- 服务监控: 实时采集机器内存和CPU的使用百分比,磁盘读写io,网络io,各个端口的响应时间等等关系到业务健康的指标。这些数据会通过网络发送到监控中心,如果某项超出正常阈值,就需要发起警报,电话短信狂轰乱炸,直到状态恢复。
- 链路跟踪: 一般对外接口会调用多个微服务。如果对外接口出现了问题,我们需要一种机制,它能够追踪这条调用链路上都调用了哪些微服务以及相应的耗时,帮助我们轻松找出性能瓶颈。
总结一下:熔断,限流,服务监控和链路跟踪。
面试官继续追问:微服务通信除了rpc和http,还有什么其他方式吗?
- HTTP / HTTPS 我们所指的通过HTTP访问, 指得是通过 域名+HTTP+JSON/XML 的方式实现服务之间的数据交互。一般需要请求负载均衡例如nginx来做域名到ip的映射和转发。
- RPC RPC是服务之间点对点的连接,即通过IP地址建立TCP链接。它并不像HTTP那样有具体的协议格式,只是提出了一类理论上的方式。
- 消息队列 会有生产者和消费者两个角色,也叫pub/sub模式。生产者不断将新消息送入消息队列,消费者则监听消息队列,不断消费消息队列推送来的消息。消息队列类似于邮差的角色,会将这些消息按序,不丢失地传递。
如果对调用耗时不敏感,且仅通知类型的请求,可以通过消息队列传递。常用的消息队列有:Kafka、RabbitMQ、ActiveMQ。
面试官:消息队列的实现也是一个大学问,高可用,高性能,持久化一个也不能丢。为了保持友好面试的氛围,下次再考你。
面试官换了个刁钻的角度发问:你们的服务是有状态还是无状态?
- 无状态服务:各个请求对于服务器来说统一无差别处理,请求自身携带了所有服务端所需要的所有参数(服务端自身不存储跟请求相关的任何数据,不包括数据库存储信息)。99%的业务服务都属于无状态服务,因为他们自身并不存储数据。在流量洪峰来临的时候,可以通过简单水平扩展+负载均衡来应对。
- 有状态服务:有状态服务在服务端保留之前请求的信息,用以处理当前请求。mysql数据库就是一个典型的有状态服务,所有的数据都存放在自身节点上,每次读请求都会读取历史写入的数据。mysql如果要实现分布式,就需要考虑到新副本之间的数据迁移和同步。
答:我们服务是在一个点的半径范围内寻找附近最近的N个司机,输入的参数有中心的坐标(经纬度),半径(以公里为单位)。由于司机数量高达数十万,单机吞吐量不能承载。所以我们基于地理位置给司机做了地理分区(GeoHash),类似于北京市可以按行政区进行划分成海淀,朝阳等等不同的区块。不同区块分布在不同的机器上,每个区块只能处理它对应的读写请求。基本上可以看作是一个基于地理位置索引分片的司机数据库。因此它是一个有状态的服务。
面试官最后超级追问:你开发的微服务满足了CAP的哪几个原则?
知识点 CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。
- 一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
- 可用性(A):保证每个请求不管成功或者失败都有响应。
- 分区容错性(P):系统中任意信息的丢失或失败不会影响系统的继续运作。 在线上服务必须24小时可用的情况下, 分区容错性是必须要保证的,偶发的机器故障必须要考虑在系统设计之内。因此实际上只有CP,AP两个设计选项。在实际应用设计中,严格的数据一致性即强一致性会带来写性能的瓶颈。想象一下,一个写入操作必须在所有数据副本都写入成功才能实现严格的数据一致性,如果遭遇网络丢包或延迟,单个写过程可能长达数百毫秒。只要其中一台机器故障没有写成功,写操作就会被阻塞,直到机器恢复并正常写入为止。在等待机器故障恢复过程中,写操作处于不可用状态。为了解决上述问题,衍生出了一个新的概念,最终一致性。
- 最终一致性:强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。 最终一致性本质上来说,是为了解决因为单一节点故障导致的写操作不可用的问题。在写过程中,我们可以选择只写master节点或者部分节点,让其他未写入的节点主动去同步最新数据。在个别节点崩溃失去响应的情况下,仍然保证了服务的高可用性。
答:接上面的微服务描述,为了保证可用性和分区容错性,对每一个地理区块,比如北京市海淀区,我们都有三个数据副本,每个数据副本分布在三台不同的云服务器上。由于司机会不断发送位置流来实时更新位置,因此我们需要高效地处理司机每一次的位置更新,如果遇到写入失败的情况,我们完全可以丢弃这次更新,因为司机的下一次位置更新写操作马上就要来了。司机位置的在三个副本上完全的数据一致性没有那么重要,因此我们允许少量副本写更新失败来交换对写操作的高性能。因此,我们使用quorum分布式机制,满足了AP特性和最终一致性。
在分布式服务中,为了实现高可用,必须将数据拷贝到不同的机器上,防止单台机器故障导致的服务不可用,这个过程就叫 replication 。quorum 属于分布式 leaderless replication 的一种实现方式。另外两种类型有:master-follower replication, multi-leader replication. 在后续的文章中,我们会有详细的介绍。
面试官:竖起大拇指,看得出来你气质不凡,学富五车,是个人才!(悄悄暗示人才们点赞)
HR: 同学,快告诉我你的联系方式!候选人,你跑不出我的五指山,哈哈哈哈哈~
面试总结
让我们愉快得与面试官告别,由课代表总结总结一下今日收获。
- 微服务特点:可维护性高,简化部署,扩展性强,技术易构,可靠性高
- 微服务架构基础组件:服务发现,服务配置中心,服务网关,熔断,限流,服务监控和链路跟踪
- 微服务通信:RPC,HTTP,消息队列
- 有状态服务和无状态服务
- CAP 原则:一致性(C),可用性(A),分区容错性(P)
我后面会继续更新《边面边学》系列,分享关于redis cluster, Kafka等数据驱动应用设计模式,从零开始跟你分享他们的设计理念。非常感谢大家耐心看到这里,你的点赞是我创作的最大动力。如果我有给你带来一丢丢的收获,求点赞! 求关注❤️ 求分享!
欢迎关注我的微信公众号,极客行路,我会在公众号中分享更多技术文章。