Scene-Design

Scene-Design,第1张

Scene-Design 场景题:设计判断论文抄袭的系统
    一类是基于字符串比较的方法;另一类是基于词频统计的方法。基于字符串比较的方法也称为数字指纹法,这类方法通过某种选取策略在文档中取一些字符串作为“指纹”,把指纹映射到Hash 表中,最后统计Hash表中相同的指纹数目或者比率,作为文本相似度依据。基于词频统计的方法也称为基于语义的方法。词频统计法源于信息检索技术中的向量空间模型,该类方法首先都要统计每篇文档中各个单词的出现次数,然后根据单词频度构成文档特征向量,最后采用点积、余弦或者类似方式度量两篇文档的特征向量,以此作为文档相似度的依据。
设计一个即时聊天的系统
    用户通过客户端进入系统,向服务器发出消息,请求登陆。服务器收到请求后,向客户端返回应答消息,表示同意接受该用户加入,并顺带将自己服务线程所在的监听端口号告诉用户。客户端按照服务器应答中给出的端口号与服务器建立稳定的连接。服务器通过该连接将当前在线用户的列表信息传给新加入的客户端。客户端获得了在线用户列表,就可以独立自主地与在线的其他用户通信了。当用户退出系统时要及时地通知服务器。
分布式系统事务一致性解决方案

分布式系统事务一致性解决方案
MQ(事务消息

举个例子,Bob向Smith转账,那我们到底是先发送消息,还是先执行扣款 *** 作?

好像都可能会出问题。如果先发消息,扣款 *** 作失败,那么Smith的账户里面会多出一笔钱。反过来,如果先执行扣款 *** 作,后发送消息,那有可能扣款成功了但是消息没发出去,Smith收不到钱。除了上面介绍的通过异常捕获和回滚的方式外,还有没有其他的思路呢?

下面以阿里巴巴的RocketMQ中间件为例,分析下其设计和实现思路。

RocketMQ第一阶段发送Prepared消息时,会拿到消息的地址,第二阶段执行本地事物,第三阶段通过第一阶段拿到的地址去访问消息,并修改状态。细心的读者可能又发现问题了,如果确认消息发送失败了怎么办?RocketMQ会定期扫描消息集群中的事物消息,这时候发现了Prepared消息,它会向消息发送者确认,Bob的钱到底是减了还是没减呢?如果减了是回滚还是继续发送确认消息呢?RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。如下图:

设计高并发的系统?
    HTML 页面静态化
    访问频率较高但内容变动较小,使用网站 HTML 静态化方案来优化访问速度。将社区
    内的帖子、文章进行实时的静态化,有更新的时候再重新静态化也是大量使用的策略。
    优势:
    一、减轻服务器负担。
    二、加快页面打开速度, 静态页面无需 访问 数据库,打开速度较动态页面有明显提高;
    三、很多搜索引擎都会优先收录静态页面,不仅被收录的快,还收录的全,容易被搜
    索引擎找到;
    四、HTML 静态页面不会受程序相关漏洞的影响,减少攻击 ,提高安全性。图片服务器和应用服务器相分离
    现在很多的网站上都会用到大量的图片,而图片是网页传输中占主要的数据量,也是影
    响网站性能的主要因素。因此很多网站都会将图片存储从网站中分离出来,另外架构一个
    或多个服务器来存储图片,将图片放到一个虚拟目录中,而网页上的图片都用一个 URL 地
    址来指向这些服务器上的图片的地址,这样的话网站的性能就明显提高了。
    优势:
    一、分担 Web 服务器的 I/O 负载-将耗费资源的图片服务分离出来,提高服务器的性能
    和稳定性。
    二、 能够专门对图片服务器进行优化-为图片服务设置有针对性的 缓存方案,减少带宽
    成本,提高访问速度。
    三、 提高网站的可扩展性-通过增加图片服务器,提高图片吞吐能力。数据库
    见“数据库部分的—如果有一个特别大的访问量到数据库上,怎么做优化?”。缓存
    尽量使用缓存,包括用户缓存,信息缓存等,多花点内存来做缓存,可以大量减少与
    数据库的交互,提高性能。
    假如我们能减少数据库频繁的访问,那对系统肯定大大有利的。比如一个电子商务系
    统的商品搜索,如果某个关键字的商品经常被搜,那就可以考虑这部分商品列表存放到缓
    存(内存中去),这样不用每次访问数据库,性能大大增加。镜像
    镜像是冗余的一种类型,一个磁盘上的数据在另一个磁盘上存在一个完全相同的副本
    即为镜像。负载均衡
    在网站高并发访问的场景下,使用负载均衡技术(负载均衡服务器)为一个应用构建
    一个由多台服务器组成的服务器集群,将并发访问请求分发到多台服务器上处理,避免单
    一服务器因负载压力过大而响应缓慢,使用户请求具有更好的响应延迟特性。并发控制
    加锁,如乐观锁和悲观锁。消息队列
    通过 mq 一个一个排队方式,跟 12306 一样。
负载均衡的几种类型
    HTTP重定向负载均衡

根据用户的HTTP请求计算一台应用集群中服务器的地址,并将此地址写入HTTP重定向响应中返回给用户,需要浏览器请求两次服务器才能完成。

    DNS域名解析负载均衡

利用DNS处理域名解析请求的同时进行负载均衡处理。在DNS中配置多个A记录,每次域名解析请求都会根据负载均衡算法计算一个不同的IP地址返回。

将负载均衡的工作转交给DNS,省掉了网站管理维护负载均衡服务器的麻烦,同时还可以使用智能DNS可以基于地理位置或者ISP来做域名解析,用户将会得到距离最近或者速度最快的一个服务器地址,这样可以加快用户的访问速度,改善性能。

这种方法的缺点,DNS是多级解析,每一级都会缓存DNS记录,如果某个服务器变动了,DNS记录更新的时间将会很长,这个速度取决于域名服务商。

一般大型网站都会使用DNS域名解析,利用域名解析作为一级负载均衡手段。你可以使用 dig <域名> 的方法查看某个域名的A记录,你会发现很多网站会有多条A记录。

    反向代理负载均衡

由于web服务器不直接对外提供访问,因此web服务器不需要使用外部IP,而反向代理服务器则需要配置双网卡和内部外部两套IP地址。

反向代理服务器转发请求是在HTTP协议层面,因此也叫应用层负载均衡,由于应用层在七层网络模型中的第七层,所以一般也称为七层负载均衡。

优点是和反向代理功服务器功能集成在一起,部署简单。

缺点是反向代理服务器是所有请求和响应的中转站,其性能可能会成为瓶颈。

    网络层负载均衡

通过修改请求目标地址进行负载均衡,网络层在七层网络层模型的第四层,所以也叫做四层负载均衡,也叫做IP层负载均衡。

请求达到负载均衡服务器后,由负载均衡服务器在 *** 作系统内核进程获取网络数据包,根据负载均衡算法得到一台真实web服务器的地址,然后修改请求的目的地址到这台真实的web服务器地址,等到web服务器处理完成后,响应数据包回到负载均衡服务器,再将数据包源地址修改为自身的IP(负载均衡服务器的IP)地址发送给用户浏览器

网络层的负载均衡在内核进程完成数据转发,有更好的性能。但是由于响应请求的流量要经过负载均衡服务器,容易成为瓶颈。

    数据链路层负载均衡

数据链路层主要处理 mac 地址,所以使用修改mac地址进行转发请求。负载均衡数据分发过程中不修改IP地址,只修改mac地址,通过配置真实物理服务器集群所有机器虚拟IP和负载均衡服务器IP地址一致,从而达到不修改数据包的源地址和目的地址就可以进行数据分发的目的。由于web服务器的服务器地址IP和数据请求目的IP地址一致,不需要通过负载均衡服务器进行地址转换,可将相应数据包直接返回用户。如果有足够的公有IP,其实web服务器也可以直接使用自己的IP响应请求,不过这样web服务器必须绑定负载均衡的虚拟IP地址(VIP),才能保证web服务器收到来自负载均衡发送的数据包。

这种方式称作三角传输模式,单臂模式,也叫做直接路由方式(DR)。使用DR方式的链路层负载均衡是目前大型网站使用最广的一种负载均衡手段。

设计高负载的系统
    应用无状态有效使用缓存应用拆分数据库拆分异步通信非结构化数据存储 ( TFS,NOSQL)监控、预警系统配置统一管理
订票系统,某车次只有一张火车票,假定有 1w 个人同时打开 12306 网站来订票,如何解决并发问题?(可扩展到任何高并发网站要考虑的 并发读写问题

使用乐观锁,乐观锁意思是不锁定表的情况下,利用业务的控制来解决并发问题,这样既保证数据的并发 可读性 ,又保证保存数据的 排他性,保证性能的同时解决了并发带来
的脏数据问题。hibernate 中实现乐观锁。(乐观锁,使用版本标识来确定读到的数据与提交时的数据是否一致。提交后修改版本标识,不一致时可以采取丢弃和再次尝试的策略。)

分布式与集群的区别是什么

分布式:一个业务分拆多个子业务,部署在不同的服务器上
集群:同一个业务,部署在多个服务器上

实时展现热门文章,比如近8小时点击量最大的文章前100名
    数据接收

客户端会为了减轻服务器的压力而选择延迟合并点击请求进行批量发送服务器肯定会有多台机器多进程部署来接受点击请求,接收到的请求在进行参数解析后,被发送到存储单元。为了减轻存储的压力,每个进程可能会使用小窗口聚合数据,每隔一小段时间将窗口内的数据聚合起来一起发给存储单元。

    数据存储

使用kafka存,ZeroCopy机制并发量很高,数据持久化在磁盘里成本低。不过kafka的数据一般是有过期时间的,如果想完全记住用户的点击以便做长期的数据分析,需要要使用hdfs了

    分布式TopN算法

用户太多,用户表按用户ID哈希分成了1024张子表。用户表里有一个字段score,表示这个用户的积分数。现在我们要计算前100名积分最多的用户以及积分数,该怎么查询?如果是单个表,一个SQL也就搞定了,如果是多个子表,你得在每个子表上都进行一次TopN查询,然后聚合结果再做一次TopN查询。子表查询可以多线程并行,提高聚合效率。

    滑动窗口

8小时的滑动窗口,意味着新的数据源源不断的进来,旧的数据时时刻刻在淘汰,在业务可以差几分钟。我们对时间片进行了切分,一分钟一个槽来进行计数,过期了8小时,移掉第一个,计算topn的帖子,维护窗口,移除过期的槽,然后统计topn,30s~60s调用一次

    定时任务

每个子节点都会有一个定时任务去负责维持统计窗口,过期失效的统计数据,计算局部的topn热帖。现在每个子节点都有了各自的局部topn热帖,那么还需要一个主节点去汇总这些局部热点,然后计算去全局热帖。

    点击去重

首先要从客户端下手,客户端本身可以过滤一部分无效点击。同一篇文章在太短的时间内被当前用户反复点击,这个模式还是很好发现的。如果间隔时间比较长,那就是读者的回味点击,属于文章的正向反馈,应该记录下来服务器还需要防止用户的防刷行为。如果缺失防刷控制,可以通过这种漏洞来使得自己的文章非法获得大量点击,进入热门文章列表,打上热门标签,被海量的用户看到,就会获得较大的经济效益,即使这篇文章内容本身吸引力并不足够。 如何解决电商网站超卖现象

超卖是什么

因为数据库底层的写 *** 作和读 *** 作可以同时进行,虽然写 *** 作默认带有隐式锁(即对同一数据不能同时进行写 *** 作)但是读 *** 作默认是不带锁的,所以当用户1去修改库存的时候,用户2依然可以读到库存为1,导致两个用户同时减一次库存,所以出现了超卖现象。

解决方案

    使用redis预减库存当库存大于0,才能更新库存update sk_goods_seckill set stock_count = stock_count - 1 where goods_id = #{goodsId} and stock_count > 0添加唯一索引,UNIQUE KEY u_uid_gid (user_id,goods_id) USING BTREE,防止同一用户同一商品下两次订单乐观锁,就是在数据库设计一个版本号的字段,每次修改都使其+1,这样在提交时比对提交前的版本号就知道是不是并发提交了,但是有个缺点就是只能是应用中控制,如果有跨应用修改同一条数据乐观锁就没办法了,这个时候可以考虑悲观锁。悲观锁,就是直接在数据库层面将数据锁死,类似于oralce中使用select xxxxx from xxxx where xx=xx for update,这样其他线程将无法提交数据。使用消息队列异步下单
有关微服务拆分思想?如何进行微服务的拆分?
    根据业务能力拆分

业务能力是业务架构模型中的一个概念。业务模型经常对应于一个业务对象,比如说:订单管理负责订单,客户管理负责客户。

    根据子域拆分

定义对应于领域驱动设计(DDD)的子域的服务。 一个领域由多个子域组成。每个子域对应了业务的不同组成部分。子域可以被这样分类:

核心: 业务的核心区分点,应用的最有价值的部分支持: 与业务是做什么的相关,但不是主要区分点。这个可以自己做或者外包。通用: 不特定于业务,理想情况下使用现成的软件来实现 如何保证接口的幂等性? 全局唯一ID

如果使用全局唯一ID,就是根据业务的 *** 作和内容生成一个全局ID,在执行 *** 作前先根据这个全局唯一ID是否存在,来判断这个 *** 作是否已经执行。如果不存在则把全局ID,存储到存储系统中,比如数据库、redis等。如果存在则表示该方法已经执行。

从工程的角度来说,使用全局ID做幂等可以作为一个业务的基础的微服务存在,在很多的微服务中都会用到这样的服务,在每个微服务中都完成这样的功能,会存在工作量重复。另外打造一个高可靠的幂等服务还需要考虑很多问题,比如一台机器虽然把全局ID先写入了存储,但是在写入之后挂了,这就需要引入全局ID的超时机制。

使用全局唯一ID是一个通用方案,可以支持插入、更新、删除业务 *** 作。但是这个方案看起来很美但是实现起来比较麻烦,下面的方案适用于特定的场景,但是实现起来比较简单。

去重表

这种方法适用于在业务中有唯一标的插入场景中,比如在以上的支付场景中,如果一个订单只会支付一次,所以订单ID可以作为唯一标识。这时,我们就可以建一张去重表,并且把唯一标识作为唯一索引,在我们实现时,把创建支付单据和写入去去重表,放在一个事务中,如果重复创建,数据库会抛出唯一约束异常, *** 作就会回滚。

插入或更新

这种方法插入并且有唯一索引的情况,比如我们要关联商品品类,其中商品的ID和品类的ID可以构成唯一索引,并且在数据表中也增加了唯一索引。这时就可以使用InsertOrUpdate *** 作。在mysql数据库中如下:

insert into goods_category (goods_id,category_id,create_time,update_time) 
       values(#{goodsId},#{categoryId},now(),now()) 
       on DUPLICATE KEY UPDATE
       update_time=now()
多版本控制

这种方法适合在更新的场景中,比如我们要更新商品的名字,这时我们就可以在更新的接口中增加一个版本号,来做幂等

boolean updateGoodsName(int id,String newName,int version);

在实现时可以如下

update goods set name=#{newName},version=#{version} where id=#{id} and version<${version}
状态机控制

这种方法适合在有状态机流转的情况下,比如就会订单的创建和付款,订单的付款肯定是在之前,这时我们可以通过在设计状态字段时,使用int类型,并且通过值类型的大小来做幂等,比如订单的创建为0,付款成功为100。付款失败为99

在做状态机更新时,我们就这可以这样控制

update `order` set status=#{status} where id=#{id} and status<#{status}
mq异步调用失败,如何保证数据一致性?
    按你的使用场景,推送数据必须得在数据创建事务成功之后执行,这里必须有个先后。你可以将推送这个 *** 作异步执行,消息队列有一搬有ack机制,确保消息没丢失。这时候监听消息队列的程序会执行推送,如果推送成功做标记。如果推送失败也标记记录时间,也可以推到另一个消息队列约定多少分钟重试。实在不行就彻底标记失败,或者回滚之前创建的数据。这个才是最终一致性。如果是并行的 *** 作,就得使用消息队列的/confirm/i机制了。
分布式锁的几种实现方式
    基于数据库表

要实现分布式锁,最简单的方式可能就是直接创建一张锁表,然后通过 *** 作该表中的数据来实现了。

我们对method_name(方法名)做了唯一性约束,这里如果有多个请求同时提交到数据库的话,数据库会保证只有一个 *** 作可以成功,那么我们就可以认为 *** 作成功的那个线程获得了该方法的锁,可以执行方法体内容。当方法执行完毕之后,想要释放锁的话就删除该记录

    基于数据库排他锁

可以通过数据库的排他锁来实现分布式锁在查询语句后面增加for update,数据库会在查询过程中给数据库表增加排他锁(这里再多提一句,InnoDB引擎在加锁的时候,只有通过索引进行检索的时候才会使用行级锁,否则会使用表级锁。这里我们希望使用行级锁,就要给method_name添加索引值得注意的是,这个索引一定要创建成唯一索引,否则会出现多个重载方法之间无法同时被访问的问题。重载方法的话建议把参数类型也加上。)。当某条记录被加上排他锁之后,其他线程无法再在该行记录上增加排他锁。

    基于缓存

比如Tair的put方法,redis的setnx方法等。并且,这些缓存服务也都提供了对数据的过期自动删除的支持,可以直接设置超时时间来控制锁的释放。

    基于Zookeeper

每个客户端对某个方法加锁时,在zookeeper上的与该方法对应的指定节点的目录下,生成一个唯一的瞬时有序节点。 判断是否获取锁的方式很简单,只需要判断有序节点中序号最小的一个。 当释放锁的时候,只需将这个瞬时节点删除即可。同时,其可以避免服务宕机导致的锁无法释放,而产生的死锁问题。 乐观锁冲突并发量很大怎么办

用消息队列一个个处理

消息队列 四种MQ区别 如何保证消息队列是高可用的
    集群,以rcoketMQ为例,有多master 模式、多master多slave异步复制模式、多 master多slave同步双写模式。

Producer 与 NameServer集群中的其中一个节点(随机选择)建立长连接,定期从 NameServer 获取 Topic 路由信息,并向提供 Topic 服务的 Broker Master 建立长连接,且定时向 Broker 发送心跳。Producer 只能将消息发送到 Broker master,但是 Consumer 则不一样,它同时和提供 Topic 服务的 Master 和 Slave建立长连接,既可以从 Broker Master 订阅消息,也可以从 Broker Slave 订阅消息。

如何保证消息不被重复消费

原因:

消费者在消费后,会发送一个确认信息给消息队列,消息队列收到后会将该消息从消息队列中删除,例如RabbitMQ是发送一个ACK确认消息,RocketMQ是返回一个CONSUME_SUCCESS成功标志,kafka每一个消息都有一个offset,kafka消费过消息后,需要提交offset,让消息队列知道自己已经消费过了。因为网络传输等等故障,确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将该消息分发给其他的消费者。

解决方法:

    消息可以使用唯一id标识如果拿到这个消息做数据库的insert *** 作。给这个消息做一个唯一主键,那么就算出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。如果拿到这个消息做redis的set的 *** 作,无论set几次结果都是一样的,set *** 作是算幂等 *** 作。使用第三方介质做消费记录。以redis为例,给消息分配一个全局id,只要消费过该消息,将< id,message>以K-V形式写入redis。那消费者开始消费前,先去redis中查询有没消费记录即可。
如何防止cookie被篡改
    如果是要防止在传输中被窃取或篡改,请使用 TLS如果是要防止客户端篡改,请在 cookie 中加入 MAC(消息验证码),比如可以使用 HMAC-SHA256 算法生成。这样,在从cookies得到数据后,再判断一下这个MAC码就可以知道整个cookies字段是否被篡改过。利用动态密码与Session+cookies双重验证
分布式 ID 生成器

一个唯一 ID 在一个分布式系统中是非常重要的一个业务属性,其中包括一些如订单 ID,消息 ID ,会话 ID,他们都有一些共有的特性:

全局唯一。趋势递增。

    基于数据库,可以利用 MySQL 中的自增属性 auto_increment 来生成全局唯一 ID,也能保证趋势递增。 但这种方式太依赖 DB,如果数据库挂了那就非常容易出问题。本地UUID生成,还可以采用 UUID 的方式生成唯一 ID,由于是在本地生成没有了网络之类的消耗,所有效率非常高。生成的 ID 是无序性的,不能做到趋势递增。由于是字符串并且不是递增,所以不太适合用作主键。采用本地时间,这种做法非常简单,可以利用本地的毫秒数加上一些业务 ID 来生成唯一ID,这样可以做到趋势递增,并且是在本地生成效率也很高。当并发量足够高的时候唯一性就不能保证了。Twitter雪花算法,可以基于 Twitter 的 Snowflake 算法来实现。它主要是一种划分命名空间的算法,将生成的 ID 按照机器、时间等来进行标志。
Redis和RabbitMQ用作消息队列的区别

可靠性

    Redis没有相应的机制保证消息的可靠消费,如果发布者发布一条消息,而没有对应的订阅者的话,这条消息将丢失,不会存在内存中。RabbitMQ具有消息消费确认机制,如果发布一条消息,还没有消费者消费该队列,那么这条消息将一直存放在队列中,直到有消费者消费了该条消息,以此可以保证消息的可靠消费。

消费者负载均衡

    Redis发布订阅模式,一个队列可以被多个消费者同时订阅,当有消息到达时,会将该消息依次发送给每个订阅者。RabbitMQ队列可以被多个消费者同时监控消费,但是每一条消息只能被消费一次,由于rabbitmq的消费确认机制,因此它能够根据消费者的消费能力而调整它的负载。

持久性

    Redis的持久化是针对于整个redis缓存的内容,它有RDB和AOF两种持久化方式(redis持久化方式,后续更新),可以将整个redis实例持久化到磁盘,以此来做数据备份,防止异常情况下导致数据丢失。RabbitMQ队列,消息,都可以选择是否持久化,持久化粒度更小,更灵活;

队列监控

    Redis没有所谓的监控平台。RabbitMQ实现了后台监控平台,可以在该平台上看到所有创建的队列的详细情况,良好的后台管理平台可以方面我们更好的使用。
总结

redis:轻量级,高并发,延迟敏感,即时数据分析、秒杀计数器、缓存等

rabbitmq:重量级,高并发,异步,批量数据异步处理、并行任务串行化,高负载任务的负载均衡等

rabbitmq是一个专门的AMQP协议队列,他的优势就在于提供可靠的队列服务,并且可做到异步,而redis主要是用于缓存的,redis的发布订阅模块,可用于实现及时性,且可靠性低的功能。

CAP原则

CAP原则又称CAP定理,指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。

分布式系统的CAP理论:理论首先把分布式系统中的三个特性进行了如下归纳:

一致性(C):所有的节点上的数据时刻保持同步。可用性(A):每个请求都能接受到一个响应,无论响应成功或失败。分区容忍性(P):系统应该能持续提供服务,即使系统内部有消息丢失。(分区)

下表是抛弃CAP中任意一项特性的场景说明:

CAP说明放弃C这里所说的放弃一致性,实际上指的是放弃数据的强一致性,而保留数据的最终一致性。这样的系统无法保证数据保持实时的一致性,但是能够承诺的是,数据最终会达到一个一致的状态放弃A这里所说的放弃可用性,一旦系统遇到网络分区或其他故障或为了保证一致性时,放弃可用性,那么受到影响的服务需要等待一定的时间,因此在等待期间系统无法对外提供正常的服务,即不可用放弃P这里所说的放弃分区容忍性,如果希望能够避免系统出现分区容错性问题,一种较为简单的做法是将所有的数据(或者仅仅是哪些与事务相关的数据)都放在一个分布式节点上。这样做虽然无法100%保证系统不会出错,但至少不会碰到由于网络分区带来的负面影响。但同时需要注意的是,放弃P的同时也就意味着放弃了系统的可扩展性

需要明确的一点是:对于一个分布式系统而言,分区容错性可以说是一个最基本的要求。因为既然是一个分布式系统,那么分布式系统中的组件必然需要被部署到不同的节点,否则也就无所谓的分布式系统了,因此必然出现子网络。而对于分布式系统而言,网络问题又是一个必定会出现的异常情况,因此分区容错性也就成为了一个分布式系统必然需要面对和解决的问题。因此系统架构师往往需要把精力花在如何根据业务特点在C(一致性)和A(可用性)之间寻求平衡。

base理论

base是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent
(最终一致性)三个短语的简写,是基于CAP定理逐步演化而来的,其核心思想是即使无法做到强一致性,但每个应用都可以根据自身的业务特点,采用适当的方法来使系统达到最终一致性。

1、基本可用

基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性,但请注意,这绝不等价于系统不可用。一下就是两个"基本可用"的例子。

响应时间上的损失:系统正常0.5秒能返回结果,由于出现故障,响应时间增加到了1~2秒功能上的损失:高并发情况下,为了保护系统的稳定性(或者保证一致性),部分消费者可能会被引导到一个降级页面(类似"高峰期,稍后再试"页面) 2、软状态

软状态是指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同的数据副本之间进行数据同步的过程存在延时。

3、最终一致性

最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。

ACID和base的区别

ACID追求强一致性,base追求最终一致性

一致性协议,从2PC到3PC到Paxos到ZAB 2PC(Two-Phase Commit,两阶段提交)

所谓的两个阶段是指:

第一阶段:准备阶段 (投票阶段)第二阶段:提交阶段(执行阶段) 1、阶段一:提交事务请求

(1)事务询问:协调者向所有的参与者发送事务内容,询问是否可以执行事务提交 *** 作,并开始等待各参与者的响应(2)执行事务:各参与者节点执行事务 *** 作,并将Undo和Redo信息计入事务日志中(3)各参与者向协调者反馈事务询问的响应:如果参与者成功执行了事务 *** 作,那么就反馈给协调者Yes响应,表示事务可以执行;如果参与者没有成功执行事务,那么就反馈给协调者No响应,表示事务不可以执行。 2、阶段二:执行事务提交

第二阶段有两种情况:

(1)执行事务提交

如果所有参与者的反馈都是Yes响应,那么

A、发送提交请求:协调者向所有参与者节点发出Commit请求B、事务提交:参与者接收到Commit请求后,会正式执行事务提交 *** 作,并在完成提交之后释放在整个事务执行期间占用的事务资源C、反馈事务提交结果:参与者在完成事务提交之后,向协调者发送ACK信息D、完成事务:协调者接收到所有参与者反馈的ACK消息后,完成事务

(2)中断事务

任何一个参与者反馈了No响应,或者在等待超时之后,协调者尚无法接收到所有参与者的反馈响应,那么就会中断事务。

A、发送回滚请求:协调者向所有参与者节点发出Rollback请求B、事务回滚:参与者接收到rollback请求后,会利用其在阶段一中记录的Undo信息来执行事务回滚 *** 作,并在完成回滚之后释放整个事务执行期间占用的资源C、反馈事务回滚结果:参与者在完成事务回滚之后,向协调者发送ACK信息D、中断事务:协调者接收到所有参与者反馈的ACK信息后,完成事务中断 优缺点

优点:原理简单、实现方便缺点:同步阻塞、单点问题、数据不一致、太过保守 3PC(Three-Phase Commit,三阶段提交)

Three-Phase Commit,三阶段提交,分为CanCommit、PreCommit、do Commit三个阶段。

为了避免在通知所有参与者提交事务时,其中一个参与者crash不一致时,就出现了三阶段提交的方式。三阶段提交在两阶段提交的基础上增加了一个preCommit的过程,当所有参与者收到preCommit后,并不执行动作,直到收到commit或超过一定时间后才完成 *** 作。

1、阶段一CanCommit

(1)事务询问:协调者向各参与者发送CanCommit的请求,询问是否可以执行事务提交 *** 作,并开始等待各参与者的响应

(2)参与者向协调者反馈询问的响应:参与者收到CanCommit请求后,正常情况下,如果自身认为可以顺利执行事务,那么会反馈Yes响应,并进入预备状态,否则反馈No。

2、阶段二PreCommit (1)执行事务预提交

如果协调者接收到各参与者反馈都是Yes,那么执行事务预提交

A、发送预提交请求:协调者向各参与者发送preCommit请求,并进入prepared阶段B、事务预提交:参与者接收到preCommit请求后,会执行事务 *** 作,并将Undo和Redo信息记录到事务日记中C、各参与者向协调者反馈事务执行的响应:如果各参与者都成功执行了事务 *** 作,那么反馈给协调者Ack响应,同时等待最终指令,提交commit或者终止abort (2)中断事务

如果任何一个参与者向协调者反馈了No响应,或者在等待超时后,协调者无法接收到所有参与者的反馈,那么就会中断事务。

A、发送中断请求:协调者向所有参与者发送abort请求B、中断事务:无论是收到来自协调者的abort请求,还是等待超时,参与者都中断事务 3、阶段三doCommit (1)执行提交

A、发送提交请求:假设协调者正常工作,接收到了所有参与者的ack响应,那么它将从预提交阶段进入提交状态,并向所有参与者发送doCommit请求B、事务提交:参与者收到doCommit请求后,正式提交事务,并在完成事务提交后释放占用的资源C、反馈事务提交结果:参与者完成事务提交后,向协调者发送ACK信息D、完成事务:协调者接收到所有参与者ack信息,完成事务 (2)中断事务

假设协调者正常工作,并且有任一参与者反馈No,或者在等待超时后无法接收所有参与者的反馈,都会中断事务

A、发送中断请求:协调者向所有参与者节点发送abort请求B、事务回滚:参与者接收到abort请求后,利用undo日志执行事务回滚,并在完成事务回滚后释放占用的资源C、反馈事务回滚结果:参与者在完成事务回滚之后,向协调者发送ack信息D、中断事务:协调者接收到所有参与者反馈的ack信息后,中断事务。 阶段三可能出现的问题:

协调者出现问题、协调者与参与者之间网络出现故障。不论出现哪种情况,最终都会导致参与者无法及时接收到来自协调者的doCommit或是abort请求,针对这种情况,参与者都会在等待超时后,继续进行事务提交(timeout后中断事务)。

优缺点

优点:降低参与者阻塞范围,并能够在出现单点故障后继续达成一致缺点:引入preCommit阶段,在这个阶段如果出现网络分区,协调者无法与参与者正常通信,参与者依然会进行事务提交,造成数据不一致。 Paxos算法

Paxos算法是基于消息传递且具有高度容错特性的一致性算法,是目前公认的解决分布式一致性问题最有效的算法之一。

Paxos 算法解决的问题是在一个可能发生上述异常的分布式系统中如何就某个值达成一致,保证不论发生以上任何异常,都不会破坏决议的一致性。一个典型的场景是,在一个分布式数据库系统中,如果各节点的初始状态一致,每个节点都执行相同的 *** 作序列,那么他们最后能得到一个一致的状态。为保证每个节点执行相同的命令序列,需要在每一条指令上执行一个「一致性算法」以保证每个节点看到的指令一致。

简单说来,Paxos的目的是让整个集群的结点对某个值的变更达成一致。Paxos算法基本上来说是个民主选举的算法——大多数的决定会成个整个集群的统一决定。任何一个点都可以提出要修改某个数据的提案,是否通过这个提案取决于这个集群中是否有超过半数的结点同意(所以Paxos算法需要集群中的结点是单数)

ZAB协议

ZAB(Zookeeper Atomic Broadcast)协议是专门为zookeeper设计的一致性协议。

ZAB协议包括两种基本的模式:消息广播和崩溃恢复

    当整个服务框架在启动过程中,或是当Leader服务器出现网络中断崩溃退出与重启等异常情况时,ZAB就会进入恢复模式并选举产生新的Leader服务器。

    当选举产生了新的Leader服务器,同时集群中已经有过半的机器与该Leader服务器完成了状态同步之后,ZAB协议就会退出崩溃恢复模式,进入消息广播模式。

    当有新的服务器加入到集群中去,如果此时集群中已经存在一个Leader服务器在负责进行消息广播,那么新加入的服务器会自动进入数据恢复模式,找到Leader服务器,并与其进行数据同步,然后一起参与到消息广播流程中去。

以上其实大致经历了三个步骤:

    崩溃恢复:主要就是Leader选举过程。数据同步:Leader服务器与其他服务器进行数据同步。消息广播:Leader服务器将数据发送给其他服务器。
ZAD和Paxos算法的联系和区别

共同点:

    两者都存在一个类似于Leader进程的角色,由其负责协调多个Follow进程的运行。Leader进程都会等待超过半数的Follower做出正确的反馈后,才会将一个提案进行提交。在ZAB协议中,每个Proposal中都包含了一个epoch值,用来代表当前Leader周期,在Paxos算法中,同样存在这样一个标识,只是名字变成了Ballot。

不同点:

Paxos算法中,一个新的选举产生的主进程会进行两个阶段的工作

    读阶段,新的主进程会通过和所有其他进程进行通信的方式来搜集上一个主进程提出的提案,并将它们提交。写阶段,当前主进程开始提出它自己的提案。

ZAB在Paxos基础上额外添加一个同步阶段。同步阶段之前,ZAB协议存在一个和Paxos读阶段类似的发现(Discovery)阶段

同步阶段中,新的Leader会确保存在过半的Follower已经提交了之前Leader周期中的所有事务Proposal发现阶段的存在,确保所有进程都已经完成对之前所有事物Proposal的提交

ZAB协议主要用于构建一个高可用的分布式数据主备系统,例如ZooKeeper,Paxos算法则是用于构建一个分布式的一致性状态机系统

服务端主动推送怎么实现
    ajax:这是通过模拟服务器发起的通信,不是实时通信, 就是需要不断的向服务器发送消息询问,这种方式会对服务器造成极大的性能浪费。comet:基于 HTTP 长连接的 “服务器推” 技术,能使服务器端主动以异步的方式向客户端程序推送数据,而不需要客户端显式的发出请求websocket: 浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
RESTful接口设计

本质:一种软件架构风格;核心:基于资源;作用:不同的客户端(如Web、微信小程序、APP)可以使用同一套API;方法类型:POST(新增数据)、DELETE(删除数据)、PUT(更新数据)、GET(得到数据)

注意

    API对应的版本号要在URL中体现。不同的HTTP动词对应不同的 *** 作。URL中尽量不要出现动词。
一致性哈希算法

一致性Hash算法将整个哈希值空间组织成一个虚拟的圆环,整个空间按顺时针方向组织,圆环的正上方的点代表0,0点右侧的第一个点代表1,以此类推,我们把这个由2^32个点组成的圆环称为Hash环。下一步将各个服务器使用Hash进行一个哈希,具体可以选择服务器的IP或主机名作为关键字进行哈希,如果一台服务器不可用,则受影响的数据仅仅是此服务器到其环空间中前一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之间数据,其它不会受到影响。

解决了什么问题?

1.使用hash存在问题,服务器数量改变,所有缓存位置都要改变
2.为了解决容错性和可扩展性,一台宕机,不会影响其他机器,只会影响前面一台
3.使用虚拟节点,解决数据倾斜问题,多了一步虚拟节点到实际节点的映射

RabbitMQ消息丢失解决

RabbitMQ消息丢失分为三种情况

1、消息在传入过程中丢失

生产者开启/confirm/i模式,每次写的消息都会分配一个唯一id,然后写入rabbitmq,rabbitmq会回传ack消息,如果丢失会回调你的一个nack接口,告诉你这个消息接收失败,结合生成者内存维护每个消息id的状态,如果超过一定时间还没接收到这个消息的回调。说明可以重发

2、消息在RabbitMQ弄丢

开启RabbitMQ持久化

3、消息在消费者丢失

关闭RabbitMQ自动ACK,确保完成再ACK,否则不要ACK

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5722150.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-18
下一篇 2022-12-18

发表评论

登录后才能评论

评论列表(0条)

保存