Yara

yzw


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

Web

发表于 2021-05-18 | 分类于 面试问题 |

OAuth2的四种模式是什么

● 授权码模式

● 隐式授权模式

● 密码模式

● 客户端凭证模式

1)授权码模式

● 访问一个 authorize 的界面,地址带上 response_type, client_id, redirect_uri, scope, state

● 用户登录后点击授权,然后跳转到 redirect_uri 的地址,地址携带授权码(code)和 state

● 网站得到 code 以后可以在后端请求获取 access token。请求携带: client_id, client_secret, grant_type, code, redirect_uri;

● 授权服务器收到 token 请求后验证 client_id 和 client_secret, code, grant_type 是固定的 authorization_code 表示授权码模式

● 将 token 发送到 redirect_uri 的地址,携带 access_token, token_type, expires_in, refresh_token
response_type 是 code

2)隐式授权模式

● 访问一个 authorize 的界面,地址带上 response_type, client_id, redirect_uri, scope

● 授权服务器将 access token 以锚点的形式发送给 redirect_uri(锚点不会发送到服务器,可以防止中间人攻击)。
response_type 是 token,只能用于一些安全要求不高的场景,会话时间短。

3)密码模式

● 直接 post 访问授权服务器,grant_type 是 password, 携带 client_id, username, password

4)客户端凭证模式

● 直接 post 访问授权服务器,grant_type 是 client_credentials, 携带 client_id, client_secret

● 授权服务器验证后直接返回 access token
这个一般是针对第三方应用的,而不是针对用户的,即可能多个用户共享同一个令牌。

怎样更新AccessToken

● 向授权服务器发送请求, client_id, client_secret, refresh_token, grant_type 是 refresh_token

● 授权服务器验证后直接返回新的 access token

OpenId认证

● 访问 authorize 地址,response_type 为 id_token 或 code id_token; client_id, redirect_uri, state

● 授权服务器将 id_token 返回。
OpenId Connect 也包括显式和隐式授权以上为隐式;id_token 通常包含用户信息,如果不包含,则应该用 accessToken 访问 userinfo 接口获取。

TCP建立和断开连接过程

TCP 使用 Flags(标志位)实现可靠连接服务,Flags 长度为 6 bits,每一位都表示一种连接状态:

● SYN:建立连接

● FIN:关闭连接

● ACK:响应

● PSH:表示有数据传输

● RST:连接重置

● URG:紧急

MSL 表示 TCP 报文最大生存时间,指任意报文在网络中存在的最大时间,超过这个时间,报文将被丢弃。
现在有主机 A 和主机 B

建立连接过程:

● A 产生一个随机数(seqA)的数据包,并将 flags 的 SYN 置为 1 发送给主机 B,表示 A要求建立连接

● B 收到请求后,也向 A 发送一个数据包,其中包含 flags (SYN=1, ACK=1),ask_number=seqA+1,和一个随机数(seqB)

● A 收到应答后,检查 ACK 码位是否为 1,ask_number 是否正确,如果正确,A 会再向 B 发送一个数据包,包含 flags (SYN=1, ACK=1),ask_number=seqB+1

● B 收响应后,检查 ACK 码位是否为 1,ask_number 是否正确,如果正确则表示建立连接成功

断开连接的过程:

● A 向 B 发送 flags(FIN=1) 和一个序列号(seq=a)的数据包,进入 FIN-WAIT-1 阶段,表示 A 没有数据要向 B 发送了

● B 收到请求后,向 A 发送 flags(ACK=1),序列号(seq=b)和确认号(ack=a+1)的数据包来确认 A 的请求,这时 B 仍可以向 A 发送数据

● B 做好断开连接的准备后,再次向 A 发送 flags(FIN=1, ACK=1),序列号(seq=c)和确认号(ack=a+1),随后 B 停止向 A 发送数据

● A 收到请求并验证,之后向 B 发送flags(ACK=1),序列号(seq=a+1)和确认号(ack=ack+1)的数据包,随后 A 等待 2 MSL 之后进入 CLOSED 阶段。

为什么断开连接客户端最后要等待 2MSL
因为最后的 ASK 包可能会丢失,服务器发现客户端没有回应 ASK,于是重新发送了 SYN,ASK 包,客户端就能在 2SML内收到重发报文,并且发送ASK报文,并重置 2SML 计时器。

TLS/SSL建立连接过程

● 客户端明文发送 协议版本、压缩算法候选列表、加密套件候选列表、随机数 等信息

● 服务端返回 协议版本、加密套件、压缩算法、另一个随机数,通知客户端信息发送结束

● 服务端将自己的 CA 证书通过响应报文发送给客户端

● 客户端验证证书是否有效(CA机构的签名,过期时间等),验证通过后,取出证书中的公钥,再生成一个随机数,并用公钥加密生成一个 key

● 客户端将 key 发送给服务端,此时客户端和服务端都使用 3 个随机数加密得到协商秘钥

● 客户端采用协商密钥与算法,将之前所有通信参数的 hash 值加密发送给服务器用于数据与握手验证

● 服务端计算之前所有接收信息的 hash 值,然后解密客户端发送的验证信息并验证

● 服务端也和客户端做同样的操作,将通信参数的 hash 值加密传给客服端

● 客户端验证通过连接建立成功

什么是透明代理

也叫强制代理,可以在不修改请求和响应的情况下重定向它们。客户端直接向真实服务器发起连接,代理机拦截请求并与客户端建立连接,然后以客户端 ip 与真实服务器进行请求转发,所以对于客户端和服务器来说,代理机都是透明的。
透明代理的作用:

● 可以通过透明代理解决地域网络延迟问题。

● 企业行为管理软件。

跨站脚本攻击

通过恶意脚本对客户端网页进行篡改,将一些隐私数据如 cookie 发送给攻击者,或将受害者重定向到一个由攻击者控制的网站进行一些恶意操作。解决方案是:

● 转义用户输入。

● 在 cookie 中使用 HttpOnly 选项,防止对 cookie 的获取

重放攻击

指攻击者发送一个目标主机已接收过的包,来达到欺骗系统的目的。解决方案是使用 https 加时间戳参数,另一种方案是请求带上一个随机数,如果随机数已使用过就证明是重放攻击。

SYN Flood 攻击

发送大量伪造的 TCP 连接请求,用假冒的IP发送海量的【第一个握手包 SYN 包】;被攻击的服务器返回第二个握手包 SYN + ACK,而对方不会回应第三个握手包,导致服务器保持大量半连接 HTTP 信道,并且默认重试5次回应第二个握手包。导致正常 SYN 包排不上队,连接不进来

并发控制

发表于 2021-05-18 | 分类于 面试问题 |

分布式锁的类型和原理

● 乐观锁

乐观锁首先假设数据没有冲突,不会阻塞对资源的修改,而是在资源被实际修改之前,检查锁定期间是否被其他进程修改,如果资源被修改了,就返回错误信息给用户,让用户决定后续操作。
● 悲观锁

在资源处理过程中,让资源处于锁定状态,其他试图修改资源的进程将被阻塞。悲观锁主要分为:

● 共享锁:又称读锁,如果资源被加上了共享锁,则只能读取而不能修改资源,其他事务也只能再对该资源加共享锁,而不能加排他锁。也就是在释放共享锁之前只能读取资源,而不能对资源做任何修改。一个共享锁可以同时被多个进程占有。

● 排他锁:又称写锁,如果资源被加上了排它锁,则该事务可以读取和修改资源,但其他事务不能再对该资源加任何锁,直到排它锁被释放为止。一个排它锁在同一时刻只能被一个进程所占有。

● 公平锁

多个线程按照申请锁的顺序获得锁,所有线程都排队等待。队列中的第一个线程获得锁,其他线程都被阻塞,直到锁被释放。

优点是:所有线程都能获得锁,不会出现饥饿现象。

缺点是:吞吐量下降

● 非公平锁
多个线程并发尝试获取锁,获取不到的线程重新进入等待状态,锁被释放后再次抢占。
优点是:吞吐量提高
缺点是:竞争可能导致某些线程一直获取不到锁。

● RedLock
RedLock 是排他锁的一种,是用于解决缓存服务(如 redis)单点崩溃造成分布式锁不可用的容灾策略。原理是,从 3 个独立的缓存服务中获取锁,如果能够从至少两个服务中获取到锁,就证明加锁成功。这样就能保证即使某个缓存节点挂掉,分布式锁服务仍然可用。
存在的问题:

● 为了避免与某个故障的节点通信时间过长。需要为加锁操作设置一个快速失败时间。

● 如何释放锁:判断该节点上的锁是不是自己设置的,如果是就删除;向所有节点发出释放锁的请求(即使认为没有从该节点获取锁也要发出请求)。

● 多个服务同时抢占锁,造成没有任何服务能够获得大部分数量的锁(3个服务抢3个锁,每个服务获得一个)。解决方案是在检测到自己没有获得大部分锁时,立即释放锁,并在随机时间后尝试重新获取

同步器的类型和原理

● 计数信号量
用来控制同时访问特定资源的线程数量(控制并发量)。计数信号量是一种具有最大许可数量的锁,其他线程从信号量获取许可,当发放的许可数量达到最大值时,资源将被锁定,其他线程无法继续获得许可,直到某个许可被释放为止。这种机制限制了资源在同一时间的并发访问量。

● CountDownLatch(wait group)
CountDownLatch 能够阻塞一个线程,并等待其他线程执行完毕后再继续执行。它的内部包含一个计数器,初始值是等待的线程数量,每当一个线程执行完毕,就将计数器减 1,当计数器被减到 0 时结束阻塞状态。

常见的并发模型

● Fork/Join 模型:将任务划分为多个小任务,并行计算,等待计算结果后 再合并为一个大任务。

● Actor 模型:是 Erlang 的并发模型;Actor 是模型中的 worker,Actor 的状态是封闭的,外部无法访问,多个 actor 之间通过异步消息传递信息,actor 可以对消息做出相应,还可以派生出新的 actor,actor 内部可以阻塞,但对于主线程是非阻塞的。

● CSP 模型:是 Golang 的并发模型,与 Actor 类似,但与 actor 不同的是,CSP 使用 channel 发送消息,而不是直接发送给 worker,CSP 的发送动作是同步阻塞的(SCP 有 buffering channel 的概念,在 buffer 未满的情况下是非阻塞的)。相对的, Actor 则是在内部维护一个消息队列,也是按顺序解析消息的,但通信过程不会阻塞。CSP 的 worker 之间没有耦合,而 Actor 可能产生耦合。

● 生产消费模型:使用缓存保存任务,开启多个线程生产任务,再开启多个线程从缓存中取出任务进行处理。生产者与消费者不存在耦合,并且可以根据生产速度调整消费者数量。

● Master-Worker 模型:Master 进程负责接收任务,并把任务分配给 Worker,worker 进程负责处理子任务,并将结果返回给 master,接收到结果后进行归纳汇总,得到最终结果。

● Promise 模型:Promise 指代用于取得计算结果的代理或容器,该容器初始并不包含结果,只有当计算完成时结果才被填充。可以通过等待或回调获取结果。

Unix 环境下的 I/O 模型

● 阻塞式 I/O:当发起 read 等操作进行读取时,内核会检查数据是否准备就绪,如果没有准备好,则线程会进入阻塞状态开始等待,直到数据准备完毕后,内核主动将数据复制到应用程序缓冲区。

● 非阻塞式 I/O:当发起 read 等操作时,内核会检查数据是否准备就绪,如果没有准备好,则立刻返回一个错误,如果检测到数据准备完毕,则返回可用状态,用户可以通知内核将数据复制到应用程序缓冲区。

● I/O 多路复用:阻塞一个线程,让内核不断轮询句柄状态,当有任何一个句柄的数据准备完毕,就会返回通知给调用方,调用方通过遍历获取可用句柄进行读取。

● 信号驱动式 I/O:内核检测到数据准备后,通过信号通知用户进程,用户进程收到信号后,从内核将数据复制到应用程序缓冲区。

● 异步I/O(AIO):内核检测到数据准备后,内核主动将数据复制到应用程序缓冲区,然后通过信号通知用户进程数据准备完毕。

为什么多路复用需要搭配非阻塞 I/O

数据可能会被其他进程读走,内核也可能会校验和丢弃某些已准备好的数据,但 select 则将这些句柄标记为已准备好,这时没有 accept 到的线程将被阻塞。

协程中调用阻塞式 I/O 会阻塞整个线程吗

会,协程是用户态的调度任务,操作系统中并没有协程的概念,所以以线程的角度看,协程实际上是同步阻塞执行的。而某些支持协程的编程语言,会将语言层面的同步阻塞语法在底层实现为操作系统层面的异步功能,从而支持协程内部的“阻塞”操作。

Php

发表于 2021-05-18 | 分类于 面试问题 |

PHP的生命周期

● 模块初始化

激活 SAPI,加载每一个扩展的模块初始化函数,初始化垃圾回收器,启动 zend 引擎,解析和读取 php.ini 配置信息,分配内存。

● 请求初始化
CLI 模式没有请求初始化,会直接跳过;apache 和 cgi 类的 SAPI 每次请求都会执行请求初始化,会调用每个扩展的请求初始化函数,初始化环境变量。

● 执行脚本

● 请求关闭
flush输出内容,发送HTTP应答header头,清理全局变量,调用每个扩展的请求关闭函数。

● 模块关闭
回调各扩展的 module shutdown 钩子函数。

程序的入口是 sapi 的 main 函数,在 main 函数中创建 sapi_module_struct,绑定钩子函数,然后调用 /main 目录中的公用函数执行。

PHP数组的原理

php 数组的核心是 hash 表,采用链表法解决 hash 冲突。PHP 数组的原理:元素单独存储在一个有序列表中,另外有一个中间表存储了数组元素在该列表中的下标。PHP 内部具体的实现方法是在初始化列表时额外分配同等长度的 uint32 片段作为索引表,从列表中间位置开始,先向前寻址索引表得到存储表下标,然后向后寻址存储表得到元素值。

当数组空间已满时,插入数组元素的操作就会触发扩容机制,首先检查已删除元素所占比例,比例达到阈值则重建索引和 bucket 链表,未达到阈值则分配一个原数组2倍大小的新数组。然后将原数组的元素复制到新数组上,重建索引(相当于将所有元素重新插入一遍)。

PHP的引用原理

当使用引用符号&赋值时,会为&操作的变量创建一个 zend_reference 结构,这个结构包含原变量的 zval 和一个 gc 结构体。当另一个变量指向此引用时,gc 中的引用计数(gc.refcount)加一。

PHP变量的内存模型

PHP变量的内存管理思路是:string, array 类型使用引用计数加写时复制方式;标量类型和 NULL 使用简单内存模型做深拷贝;object,resource 则无法复制,只能引用。
● 写时复制原理:
zend_value 中的子结构都包含 gc 结构体,赋值时 首先将变量值的引用指向右值的 zend_value 子结构,然后将 gc 的引用计数加一;当被赋值变量发生改变时,拷贝一份新的值赋给变量,原值的引用计数减一。

PHP如何释放循环引用产生的垃圾

当一个变量的引用计数减少后大于0,则该变量可能是垃圾,GC 会将这个变量收集起来,在达到一定数量时开始鉴定和回收垃圾。原理是将 value 的所有内部成员的引用计数减一,结果如果 value 本身的引用计数变成了 0,则表明全部引用来自内部成员,可以进行回收。如果 value 本身的引用计数大于 0 则表示不是垃圾,需要再将内部成员的引用计数整体加一。

PHP代码的执行流程

PHP 代码在Zend虚拟机中进行编译和执行,通过词法和语法解析,生成抽象语法树,然后将语法树提供的信息进一步构建成 opline。所有的 opline 编译后的指令集合和字面量都存储在 op_array 中,由 ZendVM 按顺序执行。
opline 是一个结构体,包含符号表、操作数、opcode 和返回值。opcode 根据操作数执行操作(计算、循环等)。

PHP输出缓冲区的过程

当调用输出函数时,如果内容长度大于 output_buffering 设置的大小,或调用 ob_flush 函数,数据会从 PHP 缓冲区进入 SAPI 缓冲区;这时如果调用 flush 函数 或 implicit_flush 配置项是开启状态,则会继续将内容刷入应用层(apache, fastCGI 等),后续将脱离PHP的控制。

分析下列代码,clone $this 有何意义?为什么不直接修改和返回 $this

1
2
3
4
5
6
public function withBody(StreamInterface $body)
{
$newThis = clone $this;
$newThis->body = $body;
return $newThis;
}

不改变当前对象而派生出新对象的意义在于:此方法对于当前对象没有副作用,是线程安全的。如果直接修改 $this,是线程不安全的,当两个线程同时调用,body 的值是不确定的。

Nosql

发表于 2021-05-18 | 分类于 面试问题 |

Redis 秒杀功能为什么不用decr

decr 是原子性的,可以防止“超卖”现象发生,但是要考虑秒杀失败时回滚的问题:库存共3件,10个人同时下单,数量为 -7,3个人通过,此时有一个人的后续步骤失败,需要回滚,但 incr 1 没有用,因为数量已经是 -7 了,变成 -6 显然不能恢复可用状态。

Redis 有哪些持久化方式

● RDB快照

在指定的时间间隔内将内存中的数据写入二进制文件,这种文件就是 RDB 格式快照,RDB 有三种持久化机制:

● save:同步持久化。执行过程对 redis 服务是阻塞的,在新的快照被创建后,如果存在旧快照则会进行原子性替换。

● bgsave:异步持久化。主进程会 fork 一个子进程用来进行持久化操作,快照创建后向主进程发送信号。
优势是:文件经过压缩,适合用来备份;恢复大数据的速度快;每一次都是全量备份。
缺点是:bgsave 生成快照时,主进程仍可修改数据,在两次备份期间修改的数据不会被保存。

● AOF文件

redis 将每一条命令追加写入到文件中,以 AOF 格式保存。利用 bgrewriteaof 命令可以重构 AOF 文件内容,就是将多条命令压缩成高效的批量命令。AOF 有两种持久化机制:

● always:同步持久化。每次数据发生改变就将命令同步写入磁盘

● everysec:异步持久化。每秒进行记录,如果服务崩溃,最多丢失一秒的数据

优势是:比 RDB 更能保证数据不丢失;AOF 格式是人类可读的,可以进行容灾处理和文件修复,比较灵活。
缺点是:AOF 文件体积比 RDB 大;恢复速度一般比 RDB 要慢。

Redis 的故障处理

● 验证持久化文件:redis提供了工具,可以扫描 AOF 文件,发现第一个不完整的命令后,会删除这条命令及之后的所有命令;但损坏的 RDB 快照文件无法修复,最好为快照文件保留多个备份,并通过计算快照的散列值来校验。

● 替换主服务器:当主服务器出现故障时,首先向从服务器发送 SAVE 命令,然后将从服的快照发送到新启动的主服务器,然后在新的主服务器向从服务器发送 SLAVEOF 命令重建主从链。另一种方法是让从服务器升级为主服务器,并为升级后的主服务器创建从服务器。

缓存系统面临的问题

● 缓存穿透:用户不断请求缓存和数据库中都没有的数据(如 id = -1),导致数据库压力过大,解决方案:

● 将数据库查不到的空值也进行缓存,但过期时间远小于正常数据的过期时间。

● 利用布隆过滤器,将数据映射到过滤器中,查询数据库之前先检查数据是否不存在。

● 缓存击穿:大量用户同时请求缓存中没有,但数据库中有的数据(可能是缓存过期了),导致数据库压力过大,解决方案:

● 设置热点数据永不过期。

● 请求数据库之前给查询操作加排他锁,阻塞其他事务的查询。

● 缓存雪崩:缓存中大批数据同时过期,当查询量大时导致数据库瞬间压力过大,解决方案:

● 设置缓存过期时间时,加上一段随机时间,防止缓存同时过期。

● 热点数据可以设置永不过期。

Redis 的数据类型和应用场景

● string

● 分布式锁

● sorted set

● 计数信号量:利用集合的唯一性和分数排序,判断元素是否超出限制数量。

● bitmap

● 用户签到:今天在一年中的天数 a,今年的总天数 n;key 的格式为年份:用户ID,实现为:key = a % n

● 用户在线状态:按用户 ID 设置位状态即可

● Bloom 过滤器:由 redis 模块提供

RabbitMQ 如何保证消息可靠传输

解决生产者消息丢失问题,可以采用事务或 Confirm 模式,其中 Confirm 的性能比事务要高。
解决消息队列消息丢失问题,可以采用消息持久化机制。
解决消费者数据丢失问题,可以采用手动确认消息的方式,在消息处理完毕之后应答即可。因为默认的自动确认机制会在收到消息时立刻回复确认,后续可能导致消息丢失。

项目中引入 RabbitMQ 需要注意什么

rabbitmq 在分布式系统中作为消息中间件存在,是一种全局服务,引入 mq 将增加系统复杂度,需要注意:

● 命名的规范:生产者与 exchange、消费者与 queue 的命名应该具有关联性,最好将命名过程进行封装自动创建,让使用者不需要关心命名问题。

● 消费者的幂等性:网络问题等因素会触发重试机制,所以消费者应该实现幂等。

● 无序性

Mysql

发表于 2021-05-14 | 分类于 面试问题 |

BTree索引的结构和原理

结构实际上是 B+ 树,叶子结点中包含了全部关键字(键值),及指向含有这些关键字记录的指针,叶子结点依关键字的大小自小而大的顺序链接;非终端结点为索引部分,仅含有其子树根结点中最大(或最小)关键字,及指针(B 树的非终节点也包含需要查找的有效信息)。

B+Tree相对于BTree的优势

● B 树的每个节点都包含全部数据,而 B+ 树的节点除了键值和指向下一页的指针以外没有其他数据,由于数据库的节点大小是固定的,因此 B+ 树结点能够存储相对于 B 树更多的键值,树的阶数更大,查询需要的磁盘 IO 次数也更少。

● B+ 的查询效率更加稳定。查询走过的结点数量相同,使得每一个查询的效率都差不多。

● 叶子节点按顺序双向链接,方便范围查询和排序。需要注意的是,叶节点的双向链表并不是 B+ 树的特性,而是 Mysql 索引的具体实现方式。

什么是分区表

分区表(partition by):根据一定规则,将一张表分解成多个部分。从逻辑上看只有一张表,但是底层却是由多个物理分区组成。
分区字段要么不包含主键或者索引列,要么包含全部主键和索引列。
分区表中无法使用外键约束。
分区类型:range,list,hash,key

四种隔离级别分别是什么

● 串行化:可避免脏读、不可重复读、幻读

● 可重复读:可避免脏读、不可重复读

● 读已提交:可避免脏读

● 读未提交:任何情况都无法保证

缩写 ACID

不可重复读和幻读的区别

不可重复读是多次读取同一条记录,发现该记录中某些列值被修改过。幻读是读取多条记录,发现结果不一致(多或少了)。

如何避免死锁

尽量避免同时锁住多个资源,如果必须这样做,应该保证所有事务都按相同的顺序锁定资源。
Innodb 的锁是逐步获取的,不会一次获取事物所需要的全部锁,所以进行范围操作时也应该保证不同事务按相同顺序锁定资源。
将 innodb_lock_wait_timeout 调小,默认是 50 秒,可以调成 5 秒。

Innodb 在什么情况下会锁表

由于 mysql 的默认隔离级别是可重复读,所以当 sql 使用范围子查询作为锁定语句的条件时就会锁住子查询里的表:

1

select * from a where id in (select id from b where id < 6) for update;
如果可以的话,将隔离级别改为读已提交即可避免这种情况。 当锁定条件没有使用到索引(进行了全表扫描)时将会锁表。还有一种情况是,当 mysql 的优化器认为即使使用了索引仍然要全表扫描时,就会直接给表加上表锁,因为表锁本身的开销比行锁小。

数据库的范式有哪些

● 第一范式:表中所有字段都是单一属性,不再往下划分。

● 第二范式:表中的非主键必须完全依赖于主键(只有在主键数量大于1时才有可能违反第二范式)

● 第三范式:表中的非主键不可传递依赖于主键。(学号,系别,系主任。系主任传递依赖于学号)

● BC范式:表中的每个决定因素都包含候选键。

仓库ID, 存储物品ID, 管理员ID, 数量。
[仓库ID, 存储物品ID] 和 [ 管理员ID, 存储物品ID] 都是候选键,但其中仓库ID 和 管理员ID 互为决定因素,但它俩都不包含候选键,所以不符合BC范式

缓存表的用途

将大表或多个表中的数据构建在一张表中,当从某些表中频繁读取小部分数据时,缓存表可以提升查询速度,但不保证实时同步,有点类似物化视图。

分库分表算法和应用场景

● 哈希取模算法:提前预估分表的数量,利用 hash 算法得到 hash 值,然后和分表数取余得出表序号。这种分表策略很常见,但后续对表的扩展会比较难做。

● 一致性哈希算法:将多个节点的 hash 值映射到 0 ~ 2^32 范围首尾相接的 hash 环上,再计算存储对象的 hash 值以顺时针方向查找遇到的第一个节点,即为该对象的存储节点。增加和删除节点时,只需要重建受影响的少数节点数据,不会影响到其他节点。采用这种方式,节点的添加和删除都比较容易,但改变节点数量可能会导致数据分配不均,可以采用多个虚拟节点映射到少数物理节点的方式缓解。

● range(field):以某个字段顺序拆分(如 时间),适合日志类的功能。

什么是MVCC

MVCC 就是多版本并发控制,通常情况下的流程是:
● 每行数据都存在一个版本,每次数据更新时都更新该版本

● 修改时Copy出当前版本随意修改,个事务之间无干扰

● 保存时比较版本号,如果成功(commit),则覆盖原记录;失败则(rollback)

就是每行都有版本号,保存时根据版本号决定是否成功。

理想的MVCC难以实现,

Mysql是怎样实现MVCC的

InnoDB 的记录中包含2个隐藏列:
● trx_id:事务 id,在对记录进行改动时将该事务的 id 赋给 trx_id

● roll_pointer:每次对记录进行改动时,都会把旧的版本写入undo log中。该列为指向旧版本undo log记录的指针。

每个事务都会被分配一个递增ID,当事务进行快照读操作时,会生成一个 Read View 用来判断当前事务能够看到哪个版本的数据。

Read View 的结构:

● m_ids:当前系统中活跃的事务id列表

● min_trx_id:最小的活跃事务id

● max_trx_id:分配给下一个事务的事务id

● creator_trx_id:生成该 Read View 的事务id

判断过程:

● trx_id=creator_id,表示当前事务正在访问它自己修改过的记录,该版本可以被当前事务访问

● trx_id<min_trx_id,表示改版本的事务已提交完毕,可访问

● trx_id>=max_trx_id,表示该版本的事务在当前 ReadView 生成之后才开启的,不可访问,通过 roll_pointer 指针找上个版本再次判断

● trx_id 在 m_ids 中,表示该版本还是活跃的,不可访问,通过 roll_pointer 指针找上个版本再次判断

● trx_id 不在 m_ids 中,表示创建 Read View 时,该版本已提交,可访问

微服务

发表于 2021-05-14 | 分类于 面试问题 |

什么是微服务

微服务架构是一种架构风格,提倡将单一应用程序划分成一组小的服务,服务之间互相协调配合。 每个服务都封装着具体业务,独立进行部署构建,服务可以使用不同的语言和组件来实现。
一般的微服务由:服务注册与发现,网关,配置中心,消息中心(消息总线),断路器 组成。

微服务的特点

低耦合,组件化,单一职责,自治,持续交付,分散治理

微服务之间是如何通讯的

● 同步调用
例如 REST、gRPC、Thrift;优点是:简单和常见,因为没有中间件代理,系统更简单。 缺点是只支持请求/响应的模式,降低了可用性,因为客户端和服务端在请求过程中必须都是可用的。

● 消息服务
例如 Kafka、RabbitMQ;优点是松耦合,提高可用性,支持很多通信机制,例如通知、发布/订阅。 缺点是消息中间件有额外的复杂性,使系统整体变的复杂。

REST和RPC的区别

RPC 的隐藏了远程调用这一动作,让用户感觉在调用本地方法。 可以选择多种协议,性能往往比 REST 要好,吐量比较大,响应时间小。适合内部服务之间的通信。 REST 是标准的 HTTP
协议,相对来说更标准,更通用,天然支持所有语言,很适合对外开放的 API,而且调用及测试都很方便。

服务发现的两种模式

● 客户端发现模式
客户端负责确定服务提供者的地址和负载均衡策略,客户端访问服务注册表,定时同步目标服务的实例地址列表,然后选择目标服务的一个可用实例地址发送请求。
优点:可以实现去中心化的通讯,避开入口造成的性能瓶颈;客户端可以自己制定负载均衡策略,相对比较灵活。 缺点:服务客户端与服务注册表耦合;SDK 对客户端代码存在入侵。

● 服务端发现模式
客户端通过负载均衡器访问目标服务,负载均衡器负责查询服务注册表并转发请求。Docker 和 K8S 都具有内置的服务注册表和服务发现机制。 优点:部署平台提供服务发现功能,服务和客户端都得到简化;服务发现功能的更新对客户端是无感知的。
缺点:后端增加了一次请求转发,增加了运维难度,负载均衡器可能成为性能瓶颈;如果服务发现功能不可用,会导致整个系统瘫痪。

分布式系统面临的问题

复杂的分布式服务之间有很多依赖关系,从而引发多种问题:

● 服务雪崩
fanout(调用关系:A->B->C)中的一环响应缓慢拖慢下游服务,导致资源越占越多,引发服务雪崩。

● 级联故障
某个依赖服务故障导致其他服务崩溃。

解决方案是:

  1. 服务熔断
    某个微服务不可用或者响应时间太长时,则会熔断该节点,快速返回客户端能够处理的”错误”的响应信息
  2. 服务降级
    在系统层面熔断某个或某一组服务,将资源留给更重要的业务。
  3. 服务限流 限流可以认为是服务降级的一种,通过限制输入和输出流量达到保护系统的目的。

服务降级与服务熔断的区别

● 触发原因不大一样。服务熔断一般是某个下游服务发生故障引起,而服务降级一般是从整体负荷考虑。

● 管理目标层次不一样。熔断是一个框架级的处理,每个服务都需要,无层级之分,而降级一般需要对业务有层级之分。

服务限流的方法

● 计数器法: 限制每秒或每分钟的请求数量。缺点是不精确,容易出现 在两个时间点交界处的过量请求。

● Leaky Bucket: 请求进入一个被限定大小的容器中,超出容量的请求被丢弃,容器按一定速率将请求发放给应用进行处理。

●
Token Bucket: 系统按一定速率,向容器中存入 token,请求获取 token 后才能被处理,如果请求过多,容器被取空时,请求将被丢弃。

什么是幂等性

幂等性是能够以同样的方式做两次,而对系统的影响保持不变,就好像它只做了一次的特性。

如何实现幂等性

● token机制
请求前获取token,并存入 redis 中;请求时将带上 token,业务接口检测 token 是否存在redis 中,存在表示第一次,不存在表示重复提交,直接返回重复标记给客户端。执行完毕后业务接口删除 redis 中的 token.
● 版本号机制
数据库写操作是原子性的,加上版本号即可实现幂等。

1
update `user` set point = point + 20, version = version + 1 where userid=1 and version=1

分布式事务解决方案

● 二阶段提交 一个事务协调者管理多个参与者的提交和回滚,分为准备阶段和提交阶段。

● 准备阶段:协调者给参与者发送准备命令,参与者准备资源,但不提交。

● 提交阶段:协调者等待所有参与者响应完成后进入第二阶段,如果所有参与者返回成功,则协调者向所有参与者发送提交命令;如果第一阶段中有任何参与者返回失败则向所有参与者发送回滚命令,然后等待参与者响应之后,事务执行完毕。
如果第二阶段执行失败,则不断重试,直到成功为止。二阶段提交的优点是强一致性,缺点是二阶段提交是同步阻塞协议,性能较差;协调者存在单点故障问题;基本上只有数据库支持。

● TCC 补偿机制 TCC指的是 Try - Confirm - Cancel 三种操作:

● Try:预留资源,这一阶段需要将事务需要的资源进行预留和锁定。 ● Confirm:实际执行操作,使用资源。

● Cancel:撤销操作,回退资源,解除锁定 调用者首先执行各服务的 try 动作,如果所有服务返回成功,则调用 confirm,如果有任意服务返回失败,则对所有服务调用 cancel。 TCC
的优点是不局限于数据库,可以跨业务系统实现事务;非阻塞协议,性能较好。缺点是对业务代码入侵较大;confirm 和 cancel 操作可能需要重试机制,必须实现幂等性,允许空回滚;不保证强一致性,而是追求最终一致性。

● 事务消息表
事务消息表是一张存储业务操作状态的表,可以用数据库实现。当事务执行时,提交的每一个业务的状态都存储在消息表中,当有某个业务执行失败,则由后台定时任务读取事务消息表不断重试,一般会有一个最大重试次数,超过最大次数后发出通知,让人工处理。优点是实现起来比较简单,缺点是必须实现幂等性;不保证强一致性。

● Saga事务模型 saga 事务模型分为多个小型事务,每个事务提交成功后,会发布一条通知来触发下一个事务执行。如果有一个事务失败,则执行之前提交成功的 所有事务的补偿操作进行回滚。saga 的实现方式分为两种:

● 基于事件
每个事务提交之后都会像系统派发一个事件,其他事务监听事件来决定是否执行或回滚。优点是各参与者之间完全解耦;缺点是在参与者很多的情况下会变得难以维护,还容易造成环形监听。

● 基于命令
单独创建一个服务作为协调者,协调者通过发送命令,接收回复的方式与其他服务进行交互,协调者根据各服务的回复消息决定提交或回滚。优点是避免了环形依赖;事务逻辑清晰,容易维护。缺点是增加了一个协调者服务。

什么是蓝绿发布

https://blog.csdn.net/lijiaocn/article/details/84276591

什么是金丝雀布

https://blog.csdn.net/lijiaocn/article/details/84276591
http://www.mabiji.com/wiki/canary.html

开始时,使用HTTP Header
匹配指定测试人员的流量到新版本上,然后当新版本内部测试通过后,可以再按百分比,将用户流量一点一点导入到新版本中观察一下运行情况,直到将流量全部导入到新版本上,最后完成升级;发现问题就立即取消升级,将流量切回到老版本。

什么是滚动发布

每次只升级一个或多个服务(非全部),加入生产环境;不断执行这个过程,直到集群中的全部旧版本完成升级。 优点:节约资源,用户无感知。 缺点:部署时间慢,发布策略较复杂,不好验证服务,不宜回滚

A/B测式和金丝雀发布的区别

金丝雀是发布策略,目标是确保新上线的系统稳定,关注的是新系统的BUG、隐患。 A/B测试是效果测试,同一时间有多个版本的服务对外开放,这些服务都达到了上线标准,A/B测试关注的是不同版本的服务效果(业务上的)。

服务的拆分原则

● 单一职责

● 避免环形依赖双向依赖

● 服务粒度适中- 演进式拆分

● 尽量避免分布事务

总体原则:当一个业务不依赖或极少依赖其他服务,有独立的业务语义,为超过两个其他服务提供接口,那就应该拆分为独立服务,还应当与业务人员协同工作。 项目前期避免依靠经验法则拆分,应该等业务稳定下来,业务边界明确后再做拆分。

JWT 的验签方式和优缺点

数字签名是通过散列算法,从 header 和 payload 部分生成摘要数据并进行加密得到。加密方式分为: ● 对称加密 通过唯一密钥进行对称加密,验签时需要将 token
发送到授权中心进行校验,优点是简单,适合单块系统;缺点是每个请求都要通过授权中心。 ● 非对称加密
通过授权中心的私钥加密,其他服务可以保存公钥,直接使用公钥验签即可。优点是可以通过公钥验签,减少授权中心压力;缺点是签名操作执行的比较慢,性能不高。

6 日志
1 分类
友情链接
  • 世界的副班长
© 2021 yara
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4