2021-03-10

1、说一下你们系统架构,组成,架构选型原因?

2、MongoDB是CAP中的哪个?Eureka是哪个?为什么?说下最终一致性的理解

  • C : Consistence一致性 各节点数据保证一致,也就需要保证写入的原子性
    ,无论从那个节点都能读取到最新数据
  • A:Availability可用性 高可用,即部分节点出问题,只要请求的节点没问题,都能成功返回,允许不是最新数据
  • P:Partition tolerance分区容错性 不同节点间容许时间差,即数据交互故障或丢失 分布式不可避免

强一致性:也就是CAP中的C,确保写入是原子的,同步的
弱一致性:只保证单节点的写入完成,其他节点的同步可以在写入完成后去同步,不保证结果
最终一致性:写入时是单节点写入完成,其他节点的同步在写入完成后去同步,需要设计合理的同步重试等逻辑流程,保证经过一定时间后,这些节点的数据能保持一致。在没有发生故障的前提下,数据达到一致状态的时间延迟,取决于网络延迟,系统负载和数据复制方案设计等因素

  • 舍弃P,则不存在分区,单机用来保证CA,就不是分布式系统了
  • 舍弃C,保证AP,即保证系统的高可用性
    案例:Eureka 选型原因:Eureka只做服务注册和服务发现,不会出现数据竞争问题,首要目的是保证高可用,保证不能因为自身的任何原因破坏服务之间本身的可连通性。只用保证最终一致性
  • 舍弃A,保证CP,即保证数据的一致性
    案例:Zookeeper 选型原因:Zookeeper本来就是用来做分布式系统一致性服务的软件,提供的功能有配置中心,名字服务,分布式锁,分布式同步,负载均衡,监听,服务注册和服务发现等功能。集群中的节点分为leader和follower,写服务只能由leader提供,还有一个角色observer,不参与master的选举。Zookeeper系统的多台服务器存储相同的数据并且每次数据更新都要所有服务器投票表决, 所以和一般的分布式系统相反,Zookeeper集群的性能会随着服务器数量的增加而下降。

MongoDB:一个replica set由一个primary和多个secondary组成
MongoDB所说的高可用性是更普世意义上的可用性:通过数据的复制和自动failover,即使发生物理故障,整个集群还是能够在短时间内恢复,继续工作,在这个意义上,确实是高可用的。

  • write-concern 写操作,MongoDB在什么情况下给予客户端响应
    { w: , j: , wtimeout: }
    w :当请求在w个节点处理成功后,就返回客户端成功。
    1 默认值,当同步primary后,返回成功
    0 收到就返回成功,不管是否处理好存储了。性能高,吞吐量大。 一般配合j使用
    1 最大设置值为节点数,设置超过可能引起堵塞
    'majority' 写入大多数节点后返回,这样可以保证写入不会被回滚。一般配合read-concern使用
    j:为true时表示 当写请求在写入journal之后才向客户端返回(journal 用来数据故障恢复和持久化数据的,它以日志方式来记录,跟binlog类似)
    wtimeout 超时时间,默认值为0

  • read-reference
    primary:默认值,表示读操作全部需要经过primary节点
    primaryPreferred:优先primary,如果primary刚好宕机,则切换secondary请求
    secondary:从机读
    secondaryPreferred:优先从机
    nearest:时延最短的节点

  • read-concern - 是为了避免脏读设计的
    local:默认值,表示读取值直接返回当前节点的最新数据
    majority:返回大多数节点已经写入的最新数据

结论:
{w:1} read-concern:local read-reference:primary 默认设置,保证了CP,数据一致性,primary宕机时,服务会短暂不可用,无法保证高可用。
{w:1} read-concern:local read-reference:非primary 无法保证CP,可能读取不到最新数据或者脏读数据,带preferred的可用性会更好
{w:1} read-concern:majority :弱一致性,就算是在primary上读取也无法读取到最新数据,因为数据可能会被回滚
{w:majority} read-concern:majority read-reference:primary 可以保证数据的一致性,读到最新数据,但是可用性会减低
{w:majority} read-concern:majority read-reference:secondary 不能保证读到最新的数据,弱一致性

3、为什么用分布式锁,分布式锁使用要考虑的问题

单机应用锁是为了防止多线程同时访问临界区共享变量时,产生竞争导致数据错乱。分布式锁就是为了解决分布式系统中,多实例多进程采用互斥的方式操作共享资源,一方面避免数据的重复处理任务的重复执行,加重幂等性判断,一方面避免产生不可避免的错误数据。
例子:
1、同一个定时任务,多实例只需要一个任务执行就好
2、用户还款行为,有一笔在处理中,前端再次触发一笔代扣

分布式锁应该考虑的问题:
1、在分布式系统的环境,一个方法在同一时间只能被一个机器的一个线程执行
2、高可用的获取锁和释放锁 - 分布式
3、高性能的获取锁和释放锁 - 快
4、具备可重入性 - 同一个方法可能自身回调
5、具备锁失效机制,防止死锁 - 一般设置超时时间
6、具备非阻塞特性,获取锁失败,立即返回

一般的实现:

  • 基于数据库:
    原理: 唯一索引插入失败特性
    缺陷:
    可用性和性能需要保证,需要多机备份,主备切换
    不具备可重入性,需要额外扩充字段记录实例和线程信息,做匹配
    没有锁失效机制,需要额外定时任务清理过期锁

  • 基于redis缓存
    原理:setnx key val; expire key val;delete key
    非阻塞的,需要自己不停轮询
    redis 获取锁的进程或线程如果挂了,只能等超时时间过

  • 基于zookeeper
    原理:一个分层的文件系统目录树结构,规定同一个目录下只能有一个唯一文件名。创建一个锁目录locknode,a先获取锁,就在locknode下创建一个临时顺序节点,获取locknode下所有子节点,看是否存在比自己小的节点,如果不存在,则获取锁成功, 如果存在,则获取失败
    获取失败的场景可以扩展为阻塞锁,在这里监听比自己次小的节点的释放,获得锁

zookeeper获取锁的进程或线程如果挂了,会自动释放该临时节点

优点:具备高可用、可重入、阻塞锁特性,可解决失效死锁问题。

缺点:因为需要频繁的创建和删除节点,性能上不如Redis方式

4、如何使用多线程,线程池使用要考虑哪些问题

5、Kafka如何使用?Kafka重复消息如何处理?啥once?

kafka消息队列的消费不可避免的会出现重复消费,需要消费者自己提交offset。而当消费者已经消费完消息,此时宕机了,这样offset未提交,就会出现重复消息。所以消费者这边一定要做好幂等处理。

消息传递语意:at most once, at least once, exactly once

kafka使用:

  • 生产者:acks 确认消息的设置
    0:表示producer不管broker的结果,不能保证消息只发送了一次 吞吐量高
    -1或者all:producer会等待broker leader写入完成,而且完成对其他副本的ISR写入后,才返回响应 吞吐量很低
    1:producer会等待broker leader写入完成,立马响应,不需要等待ISR 吞吐量高 默认值
    设置为0时保证了at most once 设置为-1或者all时,只能保证at least once,如果网络故障重试就会产生重复消息
    设置为1时同理也只能保证at least once,但是如果leader宕机是不是就丢消息了??

  • 消费者:enable.auto.commit 是否自动提交消费offset
    true:表示consumer拉取消息后自动提交offset, 默认值
    false:表示需要consumer成功消费消息后,手动提交offset
    false肯定保证的是at least once, true 有个问题就是自动提交offset可能会失败,而消息也不一定能成功消费,所以好像既不能保证at least once,也不能保证at most once??

kafka 如何保证exactly once
0.11.0之前没法保证
kafka 0.11.0.0版本引入了idempotent(幂等) producer机制,
开启参数: Producer ID enable.idempotent:true ; 在producer端设置 transcational.id为一个指定字符串

这样幂等producer只能保证单分区上无重复消息;事务可以保证多分区写入消息的完整性。??
consumer端自己管理offset提交-没法保证exactly once

6、如何加快Kafka消息的消费?比如每秒1000条消息,12台node,每条处理耗时100ms?

使用多个分区,使用多个消费者机器,每台机器消费分区数据时使用批量消费,批量消费的机器内使用多线程处理消息,如果全部处理成功,则提交offset。
部分消费成功的处理? 重复消费保证幂等性还是用分布式事务保证消费的回滚??

7、Saturn你们如何使用,部署?做了哪些自定制

8、使用过哪些设计模式?如何使用的。说下策略模式

  • 简单工厂模式:
    只对生产对象进行了抽象,简单的工厂类负责创建对象

  • 工厂模式:
    不仅对生产对象进行抽象,还需要对工厂进行抽象。如苹果工厂负责生产苹果,小米工厂负责生产小米

  • 抽象工厂模式:
    不仅对生产对象进行抽象,还需要对工厂进行抽象,而且工厂方法还能扩充生产多种对象。如 戴尔和宏碁工厂 戴尔工厂即负责生产戴尔的显示屏,还负责生产戴尔的鼠标等,宏碁工厂类似

  • 策略模式:
    对算法策略的封装,是一种行为封装
    与工厂模式的区别,工厂模式是对基于创建对象给客户端使用,像个黑盒模型;策略模式是自己指定算法策略,自己调用包装context来执行,是个白盒
    优点:
    1、都是策略接口的抽象实现,方便切换策略
    2、易于扩展,新增策略,不需要更改context等 (如果是工厂,新增的产品要能生效,需要在factory里面添加代码及相关参数等生效)
    3、避免了多重条件判断,易于维护 (如果是工厂,加一个就要在工厂方法加个ifelse)
    缺点:
    1、所有策略都需要暴露给客户端,调用者需要自己认知策略,选型
    2、策略类的维护成本随策略增加而增加

  • 适配器模式
    类适配器:继承原有类,实现适配器接口 A_Adapter extends A implements Target ,在实现Target接口时调用A的方法组装,达到适配的目的
    对象适配器:其实就是组合模式,将原有类A的实例a放在Target实例持有,直接调用a的实例方法拼接组装,达到适配的目的
    优点:
    1、复用性好 在现有类不能满足需求时,不需要为了部分改动再实现一套,直接使用现有类就好
    2、调用简单 使用者只需知道适配器接口,不用关心内部实现
    3、扩展性好 本来就是对原有类进行扩展
    4、解耦性好 不需要更改原有类
    缺点:
    1、过多的适配器,使系统凌乱,不易整体把握
    2、类适配器高,高耦合,需要继承 ;对象适配器需要使用原类的实例,使用复杂

实践:
金融产品设计:

计算方式:使用策略模式
本息计算策略: 等额本金,等额本息,等本等息
服务费计算策略:费率直乘,月供差
-- 这里数据库金融产品的属性就含有每个fee的计算策略枚举,
a:在代码里面实现一个枚举和策略类的映射,使用反射的方式使用策略类
b:使用工厂模式生产策略对象 缺点需要维护工厂

各个金额计算器:使用工厂方法模式 - 使用抽象工厂工厂模式
本金计算器(工厂),利息计算器(工厂),fee计算器(工厂)

9、MySQL使用中如果有几千万上亿的数据如何存储?分表分库后其他维度如何维护快速响应?

分库和分表,垂直和水平

  • 垂直分库
    把多表拆分到不同的数据库中,不影响表结构
    单库多表,在同一台机器上竞争磁盘空间,CPU,内存,网络IO,这时可以对不通业务或者领域的切分方式,将表分割到不同的数据库中去。其实微服务在合理的粒度切分后,就已经达到了垂直分库的目的。如还款计划表与还款表,就可以按照账务和还款行为进行切分,分布到不通的服务或者数据库中。
    优点:
    分解业务,降低耦合
    一定程度的提升IO、数据库连接数、降低单机硬件资源的瓶颈

  • 水平分库
    把同一个表中的数据拆分存储到不通的数据库中,其实在水平分库的同时必然涉及到水平分表。
    优点:
    解决了单库大数据,高并发的性能瓶颈
    IO争抢变少,减少死锁概率,提高系统稳定性
    可用性提高,可以部分实例可用

  • 垂直分表
    将单表按字段拆分,可以按字段访问频率,字段类型长度等进行拆分,如订单表有个Text的快照字段,存储数据偏长,只有在很少节点需要访问这个字段,那么我们可以将这个字段和订单id单独领出来建一个快照描述表。
    优点:
    加快常用数据的操作速度
    IO争抢变少,减少死锁概率,提高系统稳定性
    -- 为什么大字段单独列出来,大字段效率低
    1大字段数据量大,IO读取耗时更多,2单条数据量大,导致数据库存储分页频繁,(页是数据库的存储单位),单页内数据条数越少,IO效率低下,3内存量一定时,单条数据量越大,数据库能在内存缓存的条数就越少,降低了缓存命中概率。

  • 水平分表
    在同一个数据库中,将一张表中的数据按一定规则(如hash,一致性hash,时间如按年等)拆分到不同表中。
    优点:
    优化单一表数据量过大产生的性能问题
    IO争抢变少,减少死锁概率,提高系统稳定性

附美团的分库分表历程 https://tech.meituan.com/2016/11/18/dianping-order-db-sharding.html

分库分表的过程需要渐进式,逐步将数据迁移到新库新表,老库老表初期任然作为主要业务提供。

问题中的其他维度查询的问题,也给了解决方案,使用空间换时间,使用mysql的binlog构建新的一套面向需要的业务维度的数据库和表。如滴滴的订单表,可以按乘客userid切分水平分库分表,可以很好的服务乘客端。而司机端也想看自己的所有订单,则可以同步binlog生成一套以司机id为维度的分库分表,数据完整性可以根据考量删减部分。

10、负载均衡用了哪些?如何使用?

负载均衡是为了处理高并发,缓解服务器压力,服务器扩容

  • 服务端负载均衡:如Nginx,通过维护一个服务可用清单,然后通过心跳机制删除故障服务节点,客户端请求都直接发到Nginx,由它根据指定的负载均衡策略将请求转发到某个选定服务。

nginx剔除错误节点的设置:
max_fails=3 fail_timeout=30s
当某个节点上的请求失败响应(失败的定义类型proxy_next_upstream)达到3次,则会暂时将该节点剔除30s,之后再加回来,如此往复检测。
backup
当所有策略内节点都不可用时,启用该备用节点
proxy_next_upstream
error | timeout | invalid_header | http_500 | .. http_504 | http_403 ..http_429 | non_idempotent | off ...;
默认取值: proxy_next_upstream error timeout;与服务器建立连接,向其传递请求或读取响应头时发生错误或者超时

负载均衡策略(都需要探测剔除不可用的节点):

轮询round robin
随机random
优点:配置使用简单

权重weight 自己根据服务器性能,按权重比例分配转发的请求数
fair第三方插件 根据各个节点的响应时长,响应快的多发流量,响应慢的少发
优点:性能优先,可以根据节点性能调节分配请求数

哈希ip_hash 根据客户端ip使用某一hash算法转发,可以让某一ip的客户端始终打到同一节点,可以解决用户状态的留存问题,如session
哈希url_hash 根据客户端url使用hash算法转发,跟ip_hash类似
一致性hash,能在节点down掉或者新增时快速自适应调整,并且对其他hash节点的分配无影响
优点:能够保持状态,访问稳定

  • 客户端负载均衡:如Ribbon,客户端需要持有要访问的所有节点,在微服务中可以通过Eureka来进行服务注册和服务发现提供节点列表的维护, 也可以自己指定节点列表。

设置方式:
server.ribbon.NFLoadBalancerRuleClassName=
可以自定义策略,以下是系统提供的一些策略的实现

RoundRobinRule : 轮询策略 使用了AtomicInteger.compareAndSet 进行多线程控制
RandomRule:随机 Random.next

WeightedResponseTimeRule:权重策略 继承RoundRobinRule,权重的含义使用定时任务计算各个服务响应时间作为权重判断依据(具体就是将三个服务平均响应时间100,200,300生成权重[500,900,1200],随机数如果再0 ~ 500是第一个服务,500 ~ 900,第二个服务,类推) -基于响应时间,让处理快的服务器多处理请求,追求更好的利用服务器资源,做到响应时间更平衡

BestAvailableRule:最低并发策略 过滤暂时连接不可达的节点,对剩余节点遍历连接数,选择当前连接数最小的 -基于连接数,让当前处理数少的机器加重,最求更好的平衡每台节点的qps

AvailabilityFilteringRule:可用过滤策略 过滤掉不可达节点,过滤掉高并发节点(连接数超过阈值),剩余节点进行RoundRobinRule

ZoneAvoidanceRule:区域权衡策略

11、接口设计时如何考虑向下兼容性

12、你们的开发流程是怎样的

你可能感兴趣的:(2021-03-10)