十四、什么是可靠消息最终一致性方案?
可靠消息最终一致性方案。它是保证事务最终一致性的一种方案,允许数据在业务中出现短暂的不一致状态。
可靠消息最终一致性方案是指,当事务的发起方(事务参与者,也就是消息发送者)执行完本地事务后,同时发出一条消息,事务参与方(事务参与者,也就是消息的消费者)一定能够接收消息并可以成功处理自己的事务,如图所示。
这里面强调两点。
-
可靠消息:发起方一定得把消息传递到消费者。
-
最终一致性:最终发起方的业务处理和消费方的业务处理得完成,达成最终一致。
可靠消息最终一致性问题分析
上图是一个可靠消息服务最终一致性方案的流程图,从图中可以看出事务发起方将消息发送给消息中间件,事务消费方从消息中间件接收消息,事务发起方和消息中间件之间,事务消费方和消息中间件之间,都有网络通信,由于网络通信的不确定性,这块会导致数据的问题。下面针对导致的问题来分别进行解决。
(1)事务发起方本地事务和消息发送之间的原子性问题。
此问题是本地事务执行成功,消息必须发出去,否则丢弃消息,即本地事务执行成功和消息的发送成功,要么都成功,要么都失败。来一段伪代码参考一下。
powershell
begin transaction;
发送消息;
操作数据库;
commit transaction;
这种情况下,如果发送消息成功,数据库操作失败,则无法保证原子性。调换一下顺序。
powershell
begin transaction;
操作数据库;
发送消息;
commit transaction;
这种情况下,如果操作数据库出错,回滚,不影响数据;如果发送消息出错,也回滚,不影响数据。这么一看似乎可以保证原子性,但是会有一种情况,发送消息响应超时,导致数据库回滚,但是消息已经发送成功了。这时原子性还是无法保证的,这个时候就需要人工补偿了。
(2)事务消费方和消息消费的原子性问题。
此时要保证事务消费方必须能接受到消息,如果由于程序故障,导致事务消费方重启,那么需要消息中间件要有消息重发机制;由于网络延时的存在,当事务消费方消费消息成功,没有向消息中间件响应时,而消息中间件由于重发机制,会再次投递消息,就导致了消息重复消费的问题。此时在消费方要有幂等性解决方案。
十五、RocketMQ在分布式事务中如何应用的?
RocketMQ的事务消息,主要为了解决事务生产方执行业务和消息发送的原子性问题。事务消息流程如图所示。
事务消息的几种状态:
-
TransactionStatus.CommitTransaction: 提交状态,它允许消费者消费此消息。
-
TransactionStatus.RollbackTransaction: 回滚状态,它代表该消息将被删除,不允许被消费者消费。
-
TransactionStatus.Unknown: 中间状态,它代表需要检查消息队列来确定状态。
具体流程如下:
(1)发送half message。在执行本地业务之前,先向消息队列发送一条事务消息,此时叫做half message,此时消息被标记为(Prepared预备状态),此时的消息是无法被消费者消费的,需要生产者对消息进行二次确认后,消费者才能去消费它。
(2)消息队列回应half message发送成功。
(3)当事务发起方收到消息队列的成功响应之后,开始执行本地业务。
(4)如果本地事务执行成功,则向消息队列发送half message的确认,这样事务消费方就可以消费消息了。
(5)如果本地事务执行失败,则向消息队列发送half message的回滚,删除half message。事务消费方就无法消费消息。
(6)回查机制。当第4步无论是提交还是回滚,由于网络闪断,生产者应用重启等原因,导致生产者无法对消息队列中的half message进行二次确认(即上面的第4步骤,发送提交或者回滚消息)时,消息队列中的half message就不知道应该怎么办了。此时消息队列会定时扫描长期处于half message的消息,并发起一个回查机制,来确认此时的half message应该是提交还是回滚。此时,消息队列主动询问生产者该消息的最终状态(提交还是回滚),即为消息的回查机制。
十六、请说说微服务注册中心的存储结构
可以类比通讯录。
生活中的通讯录示例。
姓 名 | 电话 |
---|---|
张三 | 139xxxxxxxx |
注册中心示例
服务名 | 服务信息 |
---|---|
order-service(订单服务) | 服务的IP地址,服务端口,服务对外提供的URL等 |
可以联想到Java中常用的数据结构Map。
存储结构是用的ConcurrentHashMap,它的键值是服务名,值是个Map。而值的Map中键是服务实例ID,值是租约(租约里面包括服务信息)。
powershell
key: value
<服务名:<服务实例:ip+port,服务配置等等。>>
十七、设计一个注册中心需要写哪些接口?
大家想一下,服务启动后,是否需要向注册中心进行注册?所以注册中心需要提供一个接口,让服务调用它来进行服务的登记注册,这就是注册中心的第一个功能, 接受服务注册 。
服务注册完成后,注册中心得知道这个服务是否还是有效服务?所以此时需要服务定期地告诉注册中心,自己的工作状态(是否可用)。此时需要注册中心提供第二个功能, 接受服务心跳 。
当服务下线时,要通知注册中心自己要下线,注册中心需要提供对应的接口,来让服务调用。此时需要注册中心的第三个功能, 接受服务下线 。
如果服务挂了,没有及时通知注册中心,此时注册中心也发现服务,最近没有发送心跳。注册中心要主动剔除挂了的服务。此时需要注册中心第四个功能, 服务剔除 。
注册表中存储的信息,是要供其他服务查询的,就像通讯录一样,是要供主人查阅的,所以注册中心还需要第五个功能, 查询注册表中的服务信息 。
一般微服务中,每个服务都要避免单点故障,注册中心也要做集群,所以还要涉及到注册中心间,注册信息的同步。这就是注册中心的第六个功能, 注册中心集群间注册表的同步 。
如果让你开发,是不是也能开发出一个注册中心呢?其实本质就是一个web服务,提供上面分析的5个接口,供服务调用。这就是平时所说的 注册中心服务端 。那对应的调用注册中心的服务(业务服务),一般称之为 注册中心客户端 。
十八、谈谈你对RESTful规范的理解?
RESTful就是普通的http请求。其中REST表示Resource Representational State Transfer,直接翻译即"资源表现层状态转移"。
-
Resource代表互联网资源。所谓"资源"是网络上的一个实体,或者说网上的一个具体信息。它可以是一段文本、一首歌曲、一种服务,可以使用一个URI指向它,每种"资源"对应一个URI(统一资源标识符Uniform Resource Identifier)。
-
Representational是"表现层"意思。"资源"是一种消息实体,它可以有多种外在的表现形式,把"资源"具体呈现出来的形式叫作它的"表现层"。例如说文本可以用TXT格式进行表现,也可以使用XML格式、JSON格式和二进制格式;视频可以用MP4格式表现,也可以用AVI格式表现。URI只代表资源的实体,不代表它的形式。它的具体表现形式,应该由HTTP请求的头信息Accept和Content-Type字段指定,这两个字段是对"表现层"的描述。
-
State Transfer是指"状态转移"。客户端访问服务的过程中必然涉及数据和状态的转化。如果客户端想要操作服务端资源,必须通过某种手段,让服务器端资源发生"状态转移"。而这种转化是建立在表现层之上的,所以被称为"表现层状态转移"。客户端通过使用HTTP协议中的常用的四个动词来实现上述操作,它们分别是获取资源的GET、新建或更新资源的POST、更新资源的PUT和删除资源的DELETE。
-
RestTemplate是由Spring提供,用于封装HTTP调用,它可以简化客户端与HTTP服务器之间的交互,并且它强制使用RESTful风格。它会处理HTTP连接和关闭,只需要使用者提供服务器的地址(URL)和模板参数。
十九、分布式系统中为什么引入熔断?
如图所示,C服务和D服务调用B服务,B服务调用A服务。在下面情况1中,服务正常调用。服务在运行过程中,A服务发生故障(网络延时,服务异常,负载过大无法及时响应),系统变成了情况2。由于B服务调用A服务,A服务出故障,导致B服务调用A的代码处也出故障,此时B服务也出故障了,系统变成了情况3。以此类推,系统最终发展成A、B、C、D所有的服务都出错了,整个系统崩塌了。这就是雪崩。
微服务系统之间通过互相调用来实现业务功能,但每个系统都无法百分之百保证自身运行不出问题。在服务调用中,很可能面临依赖服务失效的问题(网络延时,服务异常,负载过大无法及时响应),导致服务雪崩,这对于一个系统来说是灾难性的。因此需要一个组件,能提供强大的容错能力,当服务发生异常时,能提供保护和控制,把影响控制在较小范围内,不要造成所有服务的雪崩。
熔断开关状态:开(走降级的方法),关闭(正常的调用),半开()
二十、请描述一下熔断和降级的区别?
从两方面来阐述。
(1)相似性:
①目的一致:都是从可用性和可靠性着想,为防止系统的整体响应缓慢甚至崩溃,而采用的技术手段。
②最终表现类似:对于两者来说,最终让用户体验到的是某些功能暂时不可达或不可用。
③粒度一致:都是服务级别的。
④自治性要求很高:熔断模式一般都是服务基于策略的自动触发,降级虽说可人工干预,但在微服务架构下,完全靠人显然不可能,开关预置、配置中心都是必要手段。
(2)区别:
①触发原因不一样:服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑。
②管理目标的层次不一样:熔断时一个框架级的处理,每个服务都需要,而降级一般有业务层级之分,例如,降级一般在服务调用的上层处理。
二十一、如何提升系统的并发能力?
分流:负载均衡,消息队列,数据库拆分
导流:缓存,cdn
并行/发:看具体业务。
二十二、你是依据什么来进行服务划分的?
进行微服务设计时,服务的数量相对于单体应用来说,会比较多(你在公司中,有多少个服务)。考虑的重点就是如何准确识别系统的隔离点,也就是系统的边界。只有每个服务的边界确定了,才能在以后的开发中做到更好的协作。识别系统的隔离点。
结合具体业务。电商。负载均衡。不得不拆。
二十三、微服务设计一般都遵循了什么原则?
(1)单一职责原则。让每个服务能独立,有界限的工作,每个服务只关注自己的业务。做到高内聚,服务和服务之间做到低耦合。
(2)服务自治原则。每个服务要能做到独立开发、独立测试、独立构建、独立部署,独立运行,与其他服务进行解耦。
(3)轻量级通信原则。让每个服务之间的调用是轻量级,并且能够跨平台、跨语言。例如采用RESTful风格,利用消息队列进行通信等。
(4)粒度进化原则。对每个服务的粒度把控,其实没有统一的标准,这个得结合解决的具体业务问题。不要过度设计。服务的粒度随着业务和用户的发展而发展。
软件是为业务服务的,好的系统不是设计出来的,而是进化出来的。
二十四、什么是最大努力通知方案?
如果接入过支付宝或者微信的支付接口,会遇到这样一种流程。例如,APP调用支付宝或微信的SDK(Software Development Kit软件开发工具包)进行了支付,钱已经从用户的支付宝或微信账户,转到了公司(开发APP的公司)的支付宝或微信账户上,但是支付系统,并不知道钱是否已经支付成功,需要支付宝或者微信回调公司的支付系统,才能进行后续的业务,如图所示。
这其实就是一个最大努力通知的解决方案。在方案中主要保证两点:
(1)有一定的消息重复通知机制。因为接收通知方(图中的我方支付系统)可能没有接收到通知,此时要有一定的机制对消息进行重复通知。
(2)消息校对机制。如果尽最大努力也没有通知到接收方,或者接收方消费消息后要再次消费,此时可由接收方主动向通知方查询消息信息来满足需求。
这种解决方案,其实针对内部系统和外部系统,有不同的做法。
(1)公司内部系统。针对公司内部系统来做的话,可以通过系统直接订阅消息队列来完成。因为都是自己的系统,直接订阅就可以。
(2)公司外部系统。针对公司外部系统来做的话,直接让消费方订阅消息队列就有点不合适了,毕竟不能让两家公司同时对一个消息队列进行操作,所以此时,可以在内部写一个程序来订阅消息队列,通过RPC的方式,调用消费方,使其被动的的接受通知消息。在接支付宝和微信时,一般都是采用这种方式。
二十五、spring cloud和Dubbo你是如何做选择的?
Spring Cloud | Dubbo | |
---|---|---|
注册中心 | Eureka,Nacos,Consul,ETCD,Zookeeper | Zookeeper |
服务间调用方式 | RESTful API 基于 http协议 | RPC基于dubbo协议 |
服务监控 | Spring Boot Admin | Dubbo-Monitor |
熔断器 | Spring Cloud Circuit Breaker | 不完善 |
网关 | Zuul,Gateway | 无 |
配置中心 | Config,Nacos | 无 |
服务追踪 | Sleuth+Zipkin | 无 |
数据流 | Spring Cloud Stream | 无 |
批量任务 | Spring Cloud Task | 无 |
消息总线 | Spring Cloud Bus | 无 |
从上面的比较可以看出,Spring Cloud的功能比Dubbo更全面,更完善,并且作为Spring 的旗舰项目,它可以与Spring其他项目无缝结合,完美对接,整个软件生态环境比较好。
Spring Cloud就像品牌机,整合在Spring的大家庭中,并做了大量的兼容性测试,保证了机器各部件的稳定。
Dubbo就像组装机,每个组件的选择自由度很高,但是如果你不是高手,如果你选择的某个组件出问题,就会导致整个机器的宕机,造成整体服务的可不用。
二十六、Ribbon的原理是什么?
使用:
powershell
@LoadBalanced
RestTemplate
重点答以下三点。
(1)通过拦截器对被注解@LoadBalanced修饰的RestTemplate进行拦截。
(2)将RestTemplate中调用的服务名,解析成具体的IP地址,由于一个服务名会对应多个地址,那么在选择具体服务地址的时候,需要做负载均衡。
(3)确定目标服务的IP和PORT后,通过Httpclient进行http的调用。
二十七、认证和授权
(1)Authentication(认证) 是验证您的身份的凭据(例如用户名/用户ID和密码),通过这个凭据,系统得以知道你就是你,也就是说系统存在你这个用户。所以,Authentication 被称为身份/用户验证。
(2)Authorization(授权) 发生在 Authentication(认证) 之后。授权,它主要掌管访问系统的权限。例如有些特定资源,只能是具有特定权限的人才能访问例如admin,有些对系统资源操作例如删除、添加、更新只能特定人才具有。
二十八、什么是Cache Aside机制?
Cache Aside(旁路缓存)是一种缓存数据更新机制,可以用于优化系统性能,缓解数据库等数据源的压力。在Cache Aside机制中,系统将缓存视为一个辅助的存储介质,当需要访问某个数据时,系统首先尝试从缓存中获取该数据,如果缓存中不存在该数据,则从数据库等数据源中获取数据,并将数据进行缓存。此时,缓存中就有了该数据的副本,下次需要访问该数据时就可以直接从缓存中获取,而无需再次查询数据库。
当进行数据更新时,除了对数据库进行更新,还需要同时更新缓存。如果只更新数据库而不更新缓存,那么下一次读取该数据时就可能会从缓存中读取到旧的数据。为了避免这种情况的发生,可以通过主动更新缓存或者让缓存过期来实现。主动更新缓存是指在更新数据库后,立即更新缓存中对应的数据。这样可以保证缓存中的数据和数据库中的数据保持一致。让缓存过期是指设置缓存数据的失效时间,当缓存数据过期后,下一次读取该数据时会从数据库中获取最新的数据,并将其更新到缓存中。
Cache Aside是一种缓存数据更新机制,可以有效地提高系统的性能,同时也可以保证数据的一致性和可靠性。在使用Cache Aside机制时需要考虑缓存的大小、缓存的策略、数据库更新时的缓存处理、缓存穿透和缓存雪崩等问题,这些都需要针对具体的应用场景进行合理的设计和调整。
二十九、什么是Read/Write Through机制
在Cache Aside中,有概率虽然很低出现数据不一致的情况,我们也用了延迟双删,但还是比较复杂。但要想避免缓存不一致的出现也很简单即进行写入操作时,直接将结果写入缓存,而再从缓存同步写入到数据提供方。等写入数据提供方操作结束后,写入操作才被返回。
这就是Read/Write Through写入机制。
在这种机制下,调用方只需要和缓存打交道,而不需要关心缓存后方的数据提供方。而由缓存来保证自身数据和数据提供方的一致性。
结论:读操作只和缓存打交道,直接读取缓存的结果;写操作的话,调用方写入缓存,再由缓存同步写入数据提供方。
和Cache-Aside的区别?
在Cache Aside机制中,数据写入缓存的操作,是由调用方的查询结果触发的,
而在Read/write through 机制中,则需要缓存在启动时,自身完成将所有数据从数据提供方读入缓存的过程(在项目启动的时候,其实初始化什么也没有,也没有什么需要读取的,一会有修改,缓存就是新的数据,也不用读)。
比较一下Cache Aside和Read/write through机制。在Cache aside中,缓存只是一个辅助的存在,即使缓存不工作,调用方也可以通过数据提供方完成所有的读写操作,正如其名,缓存在边上,像胯子。
而在Read/write through中,缓存直接对接了调用方,屏蔽了数据提供方,这就意味着缓存系统不可或缺,要求十分可靠。