持续更新中

一、说一说高并发场景下的如何实现系统限流?

实现的算法有四种,分别是计算器法、滑动窗口算法、漏桶算法、令牌桶;其中后两种使用较多, 漏桶算法:由两部分组成,漏点+桶。比如桶(队列)只能存放10个请求,以固定漏点(线程)处理请求。超出桶大小的请求则丢弃;缺点是无法应对突发流量。 令牌桶算法:独立线程(协程)以固定速率生产令牌丢尽桶里,若桶满则丢弃。客户端发起请求先从桶拿令牌,若无,则丢弃。拿到令牌的请求则可打到下游服务,缺点是:若桶设置比较大,会对下游服务压力。

二、Go-Micro各组件功能

八大组件(interface),可以根据需求重新实现

Server:向注册中心注册自己;监听客户端调用;处理Brocker所关注(订阅)的topic推送过来的消息;默认是rpc实现方式,还有grpc和http方式。

Client:请求服务的接口,封装了Transport和codec进行rpc调用,也封装了Brocker进行信息发布;默认是rpc实现方式,还有grpc和http方式。

Register:服务注册及发现,目前实现的consul,mdns, etcd,etcdv3,zookeeper,kubernetes.等等,watch会监听server的新注册节点或者消亡节点,来提醒客户端更新Service信息

Broker:消息队列处理消息的发布和订阅。比如订单完成了;只需提交订单信息给订单topic即可,其他需要的微服务(物流、营销)只需关注这个订单tpoic即可。broker默认实现方式http方式,尽量使用成熟的消息队列实现方式:kafka、nsq、rabbitmq、redis

Selector:调度器,根据何种负载策略选择后端Server并返回

Codec:消息编解码器,使用原protobuf(默认)、jsonrpc、mercury还是json等数据传输格式

Transport:服务之间通信接口,服务发送(Dial)和接收(Listen)的接口,是使用默认的实现方式还是对自己对接口进行实现均可

Service:client和server的封装,包含了一系列使用方法取初始化Server和Client,借此可以简单创建一个rpc服务。

三、什么是Hystrix?

在微服务中可能存在多个服务连锁调用,比如A->B->C。若某个服务压力暴增,可能导致后续服务连锁被压垮,所以断路器就是保证此场景,不至于一个节点发生问题导致所有节点发生问题。 Hystrix是一个分布式容错框架。可以阻止故障的连锁反应,实现熔断;调用服务失败后快速失败,实现优雅降级;提供实时监控和告警。

四、Hystrix是如何实现容错的?

资源隔离:

  • 线程隔离:Hystrix会给每个Command分配一个单独线程池,这样在进行单个服务调用的时候,就可以在独立线程池里进行,而不会对其他线程池造成影响。
  • 信号量隔离:客户端需向依赖服务发起请求时,首先要获取一个信号量才能真正发起调用,由于信号量的数量有限,当并发请求量超出信号量个数,后续请求则会拒绝,进入fallback流程。信号量隔离主要通过控制并发请求量,防止请求线程大面积阻塞,从而达到限流和防止雪崩的目的。

熔断和降级(调用服务失败后快速失败,实现优雅降级)

  • 熔断是防止异常不扩散,保证系统的稳定性
  • 降低是调用失败的补救逻辑,降低服务接口优先级,以低频率的尝试服务,而不是直接报错或者停止服务;若服务尝试成功,则逐渐回升服务接口优先级。

五、Hystrix容错具体实现流程

  • 通过HystrixCommand或者HystrixObservableCommand将所有外部系统(或称依赖)包装起来,整个包装对象是单独运行在一个线程之中(这是典型的命令模式)
  • 请求超过你定义的阈值(超时请求)
  • 为每个依赖关系维护一个小的线程池(或信号量);如果他满了,那么依赖的请求将立即被拒绝,而不是排队等待。
  • 统计成功,失败(由客户端抛出的异常),超时和线程拒绝次数。
  • 当超时达到一定次数打开断路器,可以在一定时间内停止对特定服务的所有请求(后面的请求直接熔断),如果服务的错误百分比通过阈值,手动(改配置,会实时监听)或自动关闭断路器。
  • 上面步骤出现被拒绝、链接超时或断路器打开,则直接fallback逻辑。

六、什么是服务雪崩、什么是服务限流?

1、当服务A调用服务B,服务B调用C,此时大量请求突然请求A,假如A能抗住,但是C抗不住,导致C请求堆积,导致B请求堆积,导致A不可用,这就是服务雪崩,解决方式就是服务降级和服务熔断 2、服务限流是指在高并发场景请求下,为了保护系统,对访问服务的请求数量进行控制,从而让系统不被大量请求压垮,在秒杀中,限流是非常重要的

七、什么是服务降级、什么是服务熔断?

降级是解决系统资源不足和海量业务请求之间的矛盾

  • 当系统负载过重时,可以关闭某个服务或限流某个服务来减轻系统压力。如28原则关闭非核心功能服务。

熔断是保护业务系统不被外部大流量或下游系统异常而拖垮。

  • 若开启熔断,服务A调用下游服务B异常时,上游服务为了保证自己不受影响,不再调用服务B直接返回一个结果,减轻服务A和B的压力,直至B恢复。

相同点:两者都是为了防止系统崩溃,均可使某些功能不可用。 异同点:熔断是下游服务故障触发,降级是为了降低系统负载。

八、如何拆分微服务?

拆分微服务的时候,为了尽量保证微服务的稳定,会有一些基本的准则: 1、微服务之间尽量不要有业务交叉。订单微服务不要对用户信息有操作,由用户微服务操作。 2、微服务之间只能通过接口进行服务调用,而不能绕过接口直接访问对方的数据;订单需要获取用户信息,只能调用户微服务接口。若订单微服务直接DB查数据,万一日后对表进行改造,订单微服务也要跟着改。 3、高内聚、低耦合。高内聚要求功能足够内敛,比如用户数据只能由用户微服务管理。低耦合只会对自己业务变动进行更改,对其他微服务的变动不为所动。比如订单微服务不会因用户数据变动而跟着变,保证获取信息接口是稳定的即可。

九、怎样设计出高内聚、低耦合的微服务?

高内聚低耦合,是一种从上而下指导微服务设计的方法。意思是不光体现微服务之间,也要体现在微服务的内部,MVC每一层。 例子:

  • 处理下单请求,通过订单微服务controller(组装业务的所有逻辑,dao、dto等)调用service进行用户数据获取,service具体要怎么获取,是使用 用户同步接口调用的方式还是异步事件驱动方式获取用户信息?,controller不管,返回给我个实现即可。
  • 处理完成后,物流、营销等微服务需要获取下单信息,订单微服务可不管,把订单信息往MQ一扔就完事了。需要的微服务去关注即可。这样双方的耦合就降低了。

实现高内聚低耦合的工具主要有同步的接口调用和异步的事件驱动(例如MQ)两种方式

十、有没有了解过DDD领域驱动设计?

DDD(Domain-Driver-Design)是面对软件复杂之道。随着微服务体系越来越大,业务越来越复杂,微服务体系也面临着如单体架构过于复杂的问题。所以业界需要一种思想来把微服务复杂度进行降低。

10.1 什么是领域 ?

比如有一张商品表Product

create table `product`(
	`price` float(7,2) COMMENT '商品价格'
	`store` smallint(20) COMMENT '商品库存',
)

商品服务形成一个领域,有自己的实体及实体的方法 物流服务形成一个领域,也有自己的实体及实体的方法

type Product struct{
	`price` float64 `json:"price`
}
func (o *Product )SetPrice(){} // 设置价格

type Logistics struct{
	`store` string`json:"store`
}
func (o *Order)SetStore(){} // 修改库存
10.2 什么是驱动设计 ?

像上面的例子,商品服务想要动库存必须调物流服务提供的接口,而不能跨过这个领域,自己来改DB。 驱动设计的意思是领域间通过同步的接口或者异步驱动事件进行服务调用。

十一、什么是中台?

中台就是将各个业务线中可复用的功能抽取出来,剥离个性,提取共性,形成可复用的组件。像每日优鲜、美团团购等都是由之前积累电商组件进行拼凑的应用场景,像这些组件就是中台。 中台分为业务中台、数据中台和技术中台。

  • 业务中台:例如用户管理、权限管理、移动支付等功能组件,比如支付宝的支付系统在哪都可使用。
  • 数据中台:例如芝麻信用、大数据杀熟等
  • 技术中台:封装技术框架、收银中台、风控中台等

DDD和中台结合,可以以DDD的领域界限划分不同的中台

十二、分布式事务如何处理?如何保证事务一致性?

十三、分布式、SOA、微服务之间有什么关系和区别

1、分布式是指将单体架构中各个部分拆分,然后部署到不同机器或进程中去,SOA和微服务基本都是分布式架构 2、SOA是一种面向服务的架构,系统的所有服务都注册在总线上,当调用服务时,从总线上查找服务信息,然后调用 3、微服务是一个更彻底面向服务的架构,将系统各个功能进行抽象成小小的应用程序,基本保持一个应用对应一个服务的架构

十四、项目如何保证微服务敏捷开发?微服务的链路追踪、持续集成、AB、蓝绿发布要怎么做?

敏捷开发:目的就是为了提高团队交付效率、快速迭代、快速试错。具体的内容可以通读这篇文章即可《Scrum详细开发过程》 链路追踪:为故障发生时,从众多的微服务进行排查的方式

  • 基于日志:形成全局事务ID,落地到日志文件。Filebeat-Logstash-Elasticsearch形成日志报表
  • 基于MQ:通常需要架构支持。经过流式计算形成一些可视化的结果 持续集成:例如使用Jenkins,配置主机地址和密钥,然后构建流水线,配置git地址和密钥,也可以配置一些shell脚本、构建完成动作(发邮件、触发其他流水线)等等。也可以在git上配置钩子触发流水线。 AB发布:蓝绿版本,若用k8s部署集群服务,可以修改镜像地址后再进行扩容副本,此时就会出现AB版本。意义新老版本过度。 金丝雀发布:若使用k8s部署集群服务,可修改镜像地址后进行调用k8s api进行回滚操作并立即停止,此时仅有一个少量容器接收到此消息进行版本回退出现金丝雀。意义是让部分人使用新功能,防止大面积差评、异常。

十五、什么是微服务、及优缺点?

微服务就是一种架构方式,将大型单体应用根据足够内聚功能进行拆分成较小的单元,降低系统的复杂度。 优点:

  • 高内聚低耦合:服务之间业务无关联,服务之间通过接口/事件驱动
  • 部署更灵活:每个服务应用是独立的项目,可以独立启动,无需其他服务依赖。
  • 技术更灵活:服务只要实现接口的方法即可,无需关注实现技术,利于对技术的迭代升级。
  • 提高代码复用:例如订单微服务可被物流、库存、营销等微服务调用。
  • 便于快速组件团队:新成员无需像以往关注单体系统各个部分的了解。仅需对专门的微服务进行了解即可 缺点:
  • 服务调用复杂性提高:容易出现服务雪崩等问题
  • 不利于分布式事务:尽量不使用微服务事务,建议使用事务最终一致性。例如平安证券,提现的时候你会发现钱到银行卡但是APP里仍无 改变,它会把这笔钱先变成Unavailable,待异步事件驱动方式获取到操作成功消息后,才减少这笔钱(事后对账的方式)
  • 测试、运维难度提升:业务出问题,到底是哪个微服务出错了?需要解决多个环境依赖。 微服务会随着需求的增加逐渐膨胀复杂。出现了DDD和中台解决方案

十六 、Docker的Namespace和Cgroup

NameSpace机制是一种资源隔离方案,使PID、IPC、网络等资源进行隔离,仅属于特定的Namespace Cgroup是控制容器中的进程对CPU、内存、磁盘、网络等资源控制。控制对资源的消耗限制

十七、Docker与虚拟机

1、容器不需要引导操作系统内核,因此可以在不到一秒就运行 2、容器的虚拟化无需其他软件,主机上所有容器共享主机调度程序,节省额外资源的请求 3、容器的镜像更小,使用分层的文件系统(联合文件系统UnionFS) 4、容器的资源管理通过cgroup实现,容器资源消耗不允许超出cgroup分配的

十八、Docker网络模型

host container

十九、简单介绍K8s源码,讲下你最熟悉的模块

k8s在github是这样介绍的:生产级别容器调度和管理。使用standard goalng project layout(标准golang项目布局)。 根目录下有api、cmd、build、docs目录等,其中cmd目录是存放可执行文件的入口代码,每个可执行文件对应一个main。 cmd目录里面的组件有kube-apiserver、kube-proxy、kebe-schedule、kube-controller-manager、kebelet、kubectl、kubeadm等组件

因为kubeadm是部署k8s应用的,当时产品上使用了k8s,需要做自动化部署,故翻了一下源码。《源码解读》