CAP理论是分布式领域中⾮常重要的⼀个指导理论,
C(Consistency)表示强⼀致性,
A(Availability)表示可⽤性,
P(Partition Tolerance)表示分区容错性,
CAP理论指出在⽬前的硬件条件下,⼀个分布式系统是必须要保证分区容错性的,⽽在这个前提下,分布式系统要么保证CP,要么保证AP,⽆法同时保证CAP。
分区容错性表示,⼀个系统虽然是分布式的,但是对外看上去应该是⼀个整体,不能由于分布式系统内部的某个结点挂点,或⽹络出现了故障,⽽导致系统对外出现异常。所以,对于分布式系统⽽⾔是⼀定要保证分区容错性的。
强⼀致性表示,⼀个分布式系统中各个结点之间能及时的同步数据,在数据同步过程中,是不能对外提供服务的,不然就会造成数据不⼀致,所以强⼀致性和可⽤性是不能同时满⾜的。
可⽤性表示,⼀个分布式系统对外要保证可⽤。
RPC,表示远程过程调⽤,对于Java这种⾯试对象语⾔,也可以理解为远程⽅法调⽤,RPC调⽤和HTTP调⽤是有区别的,RPC表示的是⼀种调⽤远程⽅法的⽅式,可以使⽤HTTP协议、或直接基于TCP协议来实现RPC,在Java中,我们可以通过直接使⽤某个服务接⼝的代理对象来执⾏⽅法,⽽底层则通过构造HTTP请求来调⽤远端的⽅法,所以,有⼀种说法是RPC协议是HTTP协议之上的⼀种协议,也是可以理解的。
在开发中,我们通常会需要⼀个唯⼀ID来标识数据,如果是单体架构,我们可以通过数据库的主键,或直接在内存中维护⼀个⾃增数字来作为ID都是可以的,但对于⼀个分布式系统,就会有可能会出现ID冲突,此时有以下解决⽅案:
1. uuid,这种⽅案复杂度最低,但是会影响存储空间和性能
2. 利⽤单机数据库的⾃增主键,作为分布式ID的⽣成器,复杂度适中,ID⻓度较之uuid更短,但是受到单机数据库性能的限制,并发量⼤的时候,此⽅案也不是最优⽅案
3. 利⽤redis、zookeeper的特性来⽣成id,⽐如redis的⾃增命令、zookeeper的顺序节点,这种⽅案和单机数据库(mysql)相⽐,性能有所提⾼,可以适当选⽤
4. 雪花算法,⼀切问题如果能直接⽤算法解决,那就是最合适的,利⽤雪花算法也可以⽣成分布式ID,底层原理就是通过某台机器在某⼀毫秒内对某⼀个数字⾃增,这种⽅案也能保证分布式架构的系统id唯⼀,但是只能保证趋势递增。业界存在tinyid、leaf等开源中间件实现了雪花算法。
在分布式系统中,⼀次业务处理可能需要多个应⽤来实现,⽐如⽤户发送⼀次下单请求,就涉及到订单系统创建订单、库存系统减库存,⽽对于⼀次下单,订单创建与减库存应该是要同时成功或同时失败的,但在分布式系统中,如果不做处理,就很有可能出现订单创建成功,但是减库存失败,那么解决这类问题,就需要⽤到分布式事务。常⽤解决⽅案有:
1. 本地消息表:创建订单时,将减库存消息加⼊在本地事务中,⼀起提交到数据库存⼊本地消息表,然后调⽤库存系统,如果调⽤成功则修改本地消息状态为成功,如果调⽤库存系统失败,则由后台定时任务从本地消息表中取出未成功的消息,重试调⽤库存系统
2. 消息队列:⽬前RocketMQ中⽀持事务消息,它的⼯作原理是:
a. ⽣产者订单系统先发送⼀条half消息到Broker,half消息对消费者⽽⾔是不可⻅的
b. 再创建订单,根据创建订单成功与否,向Broker发送commit或rollback
c. 并且⽣产者订单系统还可以提供Broker回调接⼝,当Broker发现⼀段时间half消息没有收到任何操作命令,则会主动调此接⼝来查询订单是否创建成功
d. ⼀旦half消息commit了,消费者库存系统就会来消费,如果消费成功,则消息销毁,分布式事务成功结束
e. 如果消费失败,则根据重试策略进⾏重试,最后还失败则进⼊死信队列,等待进⼀步处理
3. Seata:阿⾥开源的分布式事务框架,⽀持AT、TCC等多种模式,底层都是基于两阶段提交理论来实现的
Dubbo⽀持哪些负载均衡策略
1. 随机:从多个服务提供者随机选择⼀个来处理本次请求,调⽤量越⼤则分布越均匀,并⽀持按权重设置随机概率
2. 轮询:依次选择服务提供者来处理请求, 并⽀持按权重进⾏轮询,底层采⽤的是平滑加权轮询算法
3. 最⼩活跃调⽤数:统计服务提供者当前正在处理的请求,下次请求过来则交给活跃数最⼩的服务器来处理
4. ⼀致性哈希:相同参数的请求总是发到同⼀个服务提供者
1. ⾸先Dubbo会将程序员所使⽤的@DubboService注解或@Service注解进⾏解析得到程序员所定义的服务参数,包括定义的服务名、服务接⼝、服务超时时间、服务协议等等,得到⼀个ServiceBean。
2. 然后调⽤ServiceBean的export⽅法进⾏服务导出
3. 然后将服务信息注册到注册中⼼,如果有多个协议,多个注册中⼼,那就将服务按单个协议,单个注册中⼼进⾏注册
4. 将服务信息注册到注册中⼼后,还会绑定⼀些监听器,监听动态配置中⼼的变更
5. 还会根据服务协议启动对应的Web服务器或⽹络框架,⽐如Tomcat、Netty等
1、轮询法:将请求按顺序轮流地分配到后端服务器上,它均衡地对待后端的每⼀台服务器,⽽不关⼼服务器实际的连接数和当前的系统负载。
2、随机法:通过系统的随机算法,根据后端服务器的列表⼤⼩值来随机选取其中的⼀台服务器进⾏访问。由概率统计理论可以得知,随着客户端调⽤服务端的次数增多,其实际效果越来越接近于平均分配调⽤量到后端的每⼀台服务器,也就是轮询的结果。
3、源地址哈希法:源地址哈希的思想是根据获取客户端的IP地址,通过哈希函数计算得到的⼀个数值,⽤该数值对服务器列表的⼤⼩进⾏取模运算,得到的结果便是客服端要访问服务器的序号。采⽤源地址哈希法进⾏负载均衡,同⼀IP地址的客户端,当后端服务器列表不变时,它每次都会映射到同⼀台后端服务器进⾏访问。
4、加权轮询法:不同的后端服务器可能机器的配置和当前系统的负载并不相同,因此它们的抗压能⼒也不相同。给配置⾼、负载低的机器配置更⾼的权重,让其处理更多的请;⽽配置低、负载⾼的机器,给其分配较低的权重,降低其系统负载,加权轮询能很好地处理这⼀问题,并将请求顺序且按照权重分配到后端。
5、加权随机法:与加权轮询法⼀样,加权随机法也根据后端机器的配置,系统的负载分配不同的权重。不同的是,它是按照权重随机请求后端服务器,⽽⾮顺序。
6、最⼩连接数法:最⼩连接数算法⽐较灵活和智能,由于后端服务器的配置不尽相同,对于请求的处理有快有慢,它是根据后端服务器当前的连接情况,动态地选取其中当前积压连接数最少的⼀台服务器来处理当前的请求,尽可能地提⾼后端服务的利⽤效率,将负责合理地分流到每⼀台服务器。
1、采⽤⽆状态服务,抛弃session
2、存⼊cookie(有安全⻛险)
3、服务器之间进⾏ Session 同步,这样可以保证每个服务器上都有全部的 Session 信息,不过当服务器数量⽐较多的时候,同步是会有延迟甚⾄同步失败;
4、 IP 绑定策略
使⽤ Nginx (或其他复杂均衡软硬件)中的 IP 绑定策略,同⼀个 IP 只能在指定的同⼀个机器访问,但是这样做失去了负载均衡的意义,当挂掉⼀台服务器的时候,会影响⼀批⽤户的使⽤,⻛险很⼤;
5、使⽤ Redis 存储
把 Session 放到 Redis 中存储,虽然架构上变得复杂,并且需要多访问⼀次 Redis ,但是这种⽅案带来的好处也是很⼤的:
实现了 Session 共享;
可以⽔平扩展(增加 Redis 服务器);
服务器重启 Session 不丢失(不过也要注意 Session 在 Redis 中的刷新/失效机制);
不仅可以跨服务器 Session 共享,甚⾄可以跨平台(例如⽹⻚端和 APP 端)。
● 唯⼀id。每次操作,都根据操作和内容⽣成唯⼀的id,在执⾏之前先判断id是否存在,如果不存在则执⾏后续操作,并且保存到数据库或者redis等。
● 服务端提供发送token的接⼝,业务调⽤接⼝前先获取token,然后调⽤业务接⼝请求时,把token携带过去,务器判断token是否存在redis中,存在表示第⼀次请求,可以继续执⾏业务,执⾏业务完成后,最后需要把redis中的token删除
● 建去重表。将业务中有唯⼀标识的字段保存到去重表,如果表中存在,则表示已经处理过了
● 版本控制。增加版本号,当版本号符合时,才能更新数据
● 状态控制。例如订单有状态已⽀付 未⽀付 ⽀付中 ⽀付失败,当处于未⽀付的时候才允许修改为⽀付中等
将原本存储于单个数据库上的数据拆分到多个数据库,把原来存储在单张数据表的数据拆分到多张数据表中,实现数据切分,从⽽提升数据库操作性能。分库分表的实现可以分为两种⽅式:垂直切分和⽔平切分。
⽔平:将数据分散到多张表,涉及分区键,
分库:每个库结构⼀样,数据不⼀样,没有交集。库多了可以缓解io和cpu压⼒
分表:每个表结构⼀样,数据不⼀样,没有交集。表数量减少可以提⾼sql执⾏效率、减轻cpu压⼒
垂直:将字段拆分为多张表,需要⼀定的重构
分库:每个库结构、数据都不⼀样,所有库的并集为全量数据
分表:每个表结构、数据不⼀样,⾄少有⼀列交集,⽤于关联数据,所有表的并集为全量数据
UUID:简单、性能好,没有顺序,没有业务含义,存在泄漏mac地址的⻛险
数据库主键:实现简单,单调递增,具有⼀定的业务可读性,强依赖db、存在性能瓶颈,存在暴露业务 信息的⻛险
redis,mongodb,zk等中间件:增加了系统的复杂度和稳定性
雪花算法
Spring Cloud是⼀个微服务框架,提供了微服务领域中的很多功能组件,Dubbo⼀开始是⼀个RPC调⽤框架,核⼼是解决服务调⽤间的问题,Spring Cloud是⼀个⼤⽽全的框架,Dubbo则更侧重于服务调⽤,所以Dubbo所提供的功能没有Spring Cloud全⾯,但是Dubbo的服务调⽤性能⽐Spring Cloud⾼,不过Spring Cloud和Dubbo并不是对⽴的,是可以结合起来⼀起使⽤的。
1. 当服务A调⽤服务B,服务B调⽤C,此时⼤量请求突然请求服务A,假如服务A本身能抗住这些请求,但是如果服务C抗不住,导致服务C请求堆积,从⽽服务B请求堆积,从⽽服务A不可⽤,这就是服务雪崩,解决⽅式就是服务降级和服务熔断。
2. 服务限流是指在⾼并发请求下,为了保护系统,可以对访问服务的请求进⾏数量上的限制,从⽽防⽌系统不被⼤量请求压垮,在秒杀中,限流是⾮常重要的。
1. 服务熔断是指,当服务A调⽤的某个服务B不可⽤时,上游服务A为了保证⾃⼰不受影响,从⽽不再调⽤服务B,直接返回⼀个结果,减轻服务A和服务B的压⼒,直到服务B恢复。
2. 服务降级是指,当发现系统压⼒过载时,可以通过关闭某个服务,或限流某个服务来减轻系统压⼒,这就是服务降级。
相同点:
1. 都是为了防⽌系统崩溃
2. 都让⽤户体验到某些功能暂时不可⽤
不同点:
熔断是下游服务故障触发的,降级是为了降低系统负载
Kafka:
优点: 吞吐量⾮常⼤,性能⾮常好,集群⾼可⽤。
缺点:会丢数据,功能⽐较单⼀。
使⽤场景:⽇志分析、⼤数据采集
RabbitMQ:
优点: 消息可靠性⾼,功能全⾯。
缺点:吞吐量⽐较低,消息积累会严重影响性能。erlang语⾔不好定制。
使⽤场景:⼩规模场景。
RocketMQ:
优点:⾼吞吐、⾼性能、⾼可⽤,功能⾮常全⾯。
缺点:开源版功能不如云上商业版。官⽅⽂档和周边⽣态还不够成熟。客户端只⽀持java。
使⽤场景:⼏乎是全场景。
发送⽅确认机制:信道需要设置为 confirm 模式,则所有在信道上发布的消息都会分配⼀个唯⼀ ID。⼀旦消息被投递到queue(可持久化的消息需要写⼊磁盘),信道会发送⼀个确认给⽣产者(包含消息唯⼀ ID)。如果 RabbitMQ 发⽣内部错误从⽽导致消息丢失,会发送⼀条 nack(未确认)消息给⽣产者。所有被发送的消息都将被 confirm(即 ack) 或者被nack⼀次。但是没有对消息被 confirm 的快慢做任何保证,并且同⼀条消息不会既被 confirm⼜被nack 发送⽅确认模式是异步的,⽣产者应⽤程序在等待确认的同时,可以继续发送消息。当确认消息到达⽣产者,⽣产者的回调⽅法会被触发。
ConfirmCallback接⼝:只确认是否正确到达 Exchange 中,成功到达则回调ReturnCallback接⼝:消息失败返回时回调
接收⽅确认机制:消费者在声明队列时,可以指定noAck参数,当noAck=false时,RabbitMQ会等待消费者显式发回ack信号后才从内存(或者磁盘,持久化消息)中移去消息。否则,消息被消费后会被⽴即删除。
消费者接收每⼀条消息后都必须进⾏确认(消息接收和消息确认是两个不同操作)。只有消费者确认了消息,RabbitMQ 才能安全地把消息从队列中删除。
RabbitMQ不会为未ack的消息设置超时时间,它判断此消息是否需要重新投递给消费者的唯⼀依据是消费该消息的消费者连接是否已经断开。这么设计的原因是RabbitMQ允许消费者消费⼀条消息的时间可以很⻓。保证数据的最终⼀致性;
如果消费者返回ack之前断开了链接,RabbitMQ 会重新分发给下⼀个订阅的消费者。(可能存在消息重复消费的隐患,需要去重)
Kafka为什么吞吐量⾼
Kafka的⽣产者采⽤的是异步发送消息机制,当发送⼀条消息时,消息并没有发送到Broker⽽是缓存起来,然后直接向业务返回成功,当缓存的消息达到⼀定数量时再批量发送给Broker。这种做法减少了⽹络io,从⽽提⾼了消息发送的吞吐量,但是如果消息⽣产者宕机,会导致消息丢失,业务出错,所以理论上kafka利⽤此机制提⾼了性能却降低了可靠性。
1. 解耦:使⽤消息队列来作为两个系统之间的通讯⽅式,两个系统不需要相互依赖了
2. 异步:系统A给消息队列发送完消息之后,就可以继续做其他事情了
3. 流量削峰:如果使⽤消息队列的⽅式来调⽤某个系统,那么消息将在队列中排队,由消费者⾃⼰控制消费速度
两个误区:
1. 放⻜⾃我,漫⽆边际。
2. 纠结技术细节。
好的⽅式:
1. 从整体到细节,从业务场景到技术实现。
2. 以现有产品为基础。
答题思路: MQ作⽤、项⽬⼤概的样⼦。
1. 实现⼀个单机的队列数据结构。 ⾼效、可扩展。
2. 将单机队列扩展成为分布式队列。- 分布式集群管理
3. 基于Topic定制消息路由策略。- 发送者路由策略,消费者与队列对应关系,消费者路由策略
4. 实现⾼效的⽹络通信。- Netty Http
5. 规划⽇志⽂件,实现⽂件⾼效读写。- 零拷⻉,顺序写。 服务重启后,快速还原运⾏现场。
6. 定制⾼级功能,死信队列、延迟队列、事务消息等等。 - 贴合实际,随意发挥。
认证: 就是对系统访问者的身份进⾏确认。
授权:就是对系统访问者的⾏为进⾏控制。授权通常是在认证之后,对系统内的⽤户隐私数据进⾏保护。后台接⼝访问权限、前台控件的访问权限。
RBAC模型: 主体 -> ⻆⾊ -> 资源 -> 访问系统的⾏为。
认证和授权也是对⼀个权限认证框架进⾏扩展的两个主要的⽅⾯。
如果没有客户端的Cookie,Session是⽆法进⾏身份验证的。
CSRF: Cross Site Requst Forgery 跨站请求伪造,⼀个正常的请求会将合法⽤户的session id保存到浏览器的cookie。这时候,如果⽤户在浏览器中打来另⼀个tab⻚, 那这个tab⻚也是可以获得浏览器的cookie。⿊客就可以利⽤这个cookie信息进⾏攻击。
攻击过程:
1. 某银⾏⽹站A可以以GET请求的⽅式发起转账操作。 www.xxx.com/transfor.do?accountNum=100&money=1000 accountNum表示⽬标账户。这个请求肯定是需要登录才可以正常访问的。
2. 攻击者在某个论坛或者⽹站上,上传⼀个图⽚,链接地址是 www.xxx.com/transfer.do?accountNum=888&money=10000 其中这个accountNum就是攻击者⾃⼰的银⾏账户。
3. 如果有⼀个⽤户,登录了银⾏⽹站,然后⼜打开浏览器的另⼀个tab⻚,点击了这个图⽚。这时,银⾏就会受理到⼀个带了正确cookie的请求,就会完成转账。⽤户的钱就被盗了。
CSRF防⽌⽅式:
1. 尽量使⽤POST请求,限制GET请求。POST请求可以带请求体,攻击者就不容易伪造出请求。
2. 将cookie设置为HttpOnly : respose.setHeader("SetCookie","cookiename=cookievalue;HttpOnly")。
3. 增加token;
4. 在请求中放⼊⼀个攻击者⽆法伪造的信息,并且该信息不存在于cookie当中。这也是Spring Security框架中采⽤的防范⽅式
OAuth2.0是⼀个开放标准,允许⽤户授权第三⽅应⽤程序访问他们存储在另外的服务提供者上的信息,⽽不需要将⽤户名和密码提供给第三⽅应⽤或分享他们数据的所有内容。
OAuth2.0协议的认证流程,简单理解,就是允许我们将之前的授权和认证过程交给⼀个独⽴的第三⽅进⾏担保。
OAuth2.0协议有四种认证⽅式:
1. 授权码模式
2. 简化模式
3. 密码模式
4. 客户端模式
OAuth2.0的使⽤场景通常称为联合登录, ⼀处注册,多处使⽤
SSO Single Sign On 单点登录。 ⼀处登录,多处同时登录
SSO的实现关键是将Session信息集中存储
TCP协议是7层⽹络协议中的传输层协议,负责数据的可靠传输。
在建⽴TCP连接时,需要通过三次握⼿来建⽴,过程是:
1. 客户端向服务端发送⼀个SYN
2. 服务端接收到SYN后,给客户端发送⼀个SYN_ACK
3. 客户端接收到SYN_ACK后,再给服务端发送⼀个ACK
在断开TCP连接时,需要通过四次挥⼿来断开,过程是:
1. 客户端向服务端发送FIN
2. 服务端接收FIN后,向客户端发送ACK,表示我接收到了断开连接的请求,客户端你可以不发数据了,不过服务端这边可能还有数据正在处理
3. 服务端处理完所有数据后,向客户端发送FIN,表示服务端现在可以断开连接
4. 客户端收到服务端的FIN,向服务端发送ACK,表示客户端也会断开连接了
1. 浏览器解析⽤户输⼊的URL,⽣成⼀个HTTP格式的请求
2. 先根据URL域名从本地hosts⽂件查找是否有映射IP,如果没有就将域名发送给电脑所配置的DNS进⾏域名解析,得到IP地址
3. 浏览器通过操作系统将请求通过四层⽹络协议发送出去
4. 途中可能会经过各种路由器、交换机,最终到达服务器
5. 服务器收到请求后,根据请求所指定的端⼝,将请求传递给绑定了该端⼝的应⽤程序,⽐如8080被tomcat占⽤了
6. tomcat接收到请求数据后,按照http协议的格式进⾏解析,解析得到所要访问的servlet
7. 然后servlet来处理这个请求,如果是SpringMVC中的DispatcherServlet,那么则会找到对应的Controller中的⽅法,并执⾏该⽅法得到结果
8. Tomcat得到响应结果后封装成HTTP响应的格式,并再次通过⽹络发送给浏览器所在的服务器
9. 浏览器所在的服务器拿到结果后再传递给浏览器,浏览器则负责解析并渲染
跨域是指浏览器在发起⽹络请求时,会检查该请求所对应的协议、域名、端⼝和当前⽹⻚是否⼀致,如果不⼀致则浏览器会进⾏限制,⽐如在www.baidu.com的某个⽹⻚中,如果使⽤ajax去访问www.jd.com是不⾏的,但是如果是img、iframe、script等标签的src属性去访问则是可以的,之所以浏览器要做这层限制,是为了⽤户信息安全。但是如果开发者想要绕过这层限制也是可以的:
1. response添加header,⽐如resp.setHeader("Access-Control-Allow-Origin", "*");表示可以访问所有⽹站,不受是否同源的限制
2. jsonp的⽅式,该技术底层就是基于script标签来实现的,因为script标签是可以跨域的
3. 后台⾃⼰控制,先访问同域名下的接⼝,然后在接⼝中再去使⽤HTTPClient等⼯具去调⽤⽬标接⼝
4. ⽹关,和第三种⽅式类似,都是交给后台服务来进⾏跨域访问
下一篇:防火墙nat理论讲解