凤凰架构总结
重温了一遍周志明老师的《凤凰架构》,一方面是加深记忆一下里面的知识点,另外就是做个记录总结,方便后面忘记了在看。
全书一共有十六个章节,每个章节都相对独立又和后文有些关系。个人总结主要是围绕着微服务、架构演进以及容器编排等技术的发展来讲述的。很详细也很透彻,第一遍读的时候因为很多概念不是很清楚,比较耗时。读了后再回过头来看时候,发现顺利了不少。
第一章服务架构演进,从原始分布式时代到单体架构历史,SOA时代以及微服务时代,以及当前的云生无服务时代这些概念。主要是需要清楚里面的几个概念:
SOA:最早在1994年提出,当时不具备发展提交。直到2006年OSOA联盟的倡议与支持下,成立了Open CSA 组织。里面提出了很多的概念、思想都在今天的微服务中找到对应的身影,譬如服务之前的松散耦合、注册、发现、治理、隔离、编排等。
微服务时代:微服务最早2005年提出,指的是专注于单一职责的、和语音无关的细粒度web服务。但是一直到2014年,和SOA划清界限之后,才真正的崛起了。 现代的概念是:微服务是一种通过多个小型服务组合来构建单个应用的架构风格,这些服务围绕业务能力而非特定的技术标准来构建,各个服务可以采用不同的编程语言、不同的数据存储技术,运行在不同的进程之后。
后微服务时代:主要是以docker为代表的容器化技术的崛起,以及kubernetes成为容器编排解决的首要选择,标志着后微服务时代的开启。以kuberneters在集群中对外提供服务,以虚拟化容器技术对外提供方案的容器编排技术的完善和发展,以及服务网格(Service mesh)等技术的引入,微服务的概念也逐渐越加成熟。
无服务时代:主要是以云原生为主的元计算方面。也是作者预测以后云计算使用的一种主要的形式。
另外需要说下Sidecar Proxy的边车代理模式,这个是服务网格(Service mesh)里面用到的一种模式,下面很多章节都有介绍。
Sidecar Proxy(边车代理):在虚拟化场景中,边车指的是通信代理服务器,以类似网络安全里面中间人共计的方式进行流量劫持,在应用毫无感知的情况下,悄然接管应用所有对外通信。这个代理除了实现正常的服务间通信为(称为数据平面通信),还接收来自控制器的指令(称为控制平面通信),实现熔断、认证、度量、监控以及负载均衡等各种附加功能。 我理解就是不需要像传统方式那样,比如说一个java语音的程序,但是需要再Python或者go平台运行,传统方式需要一个jar包或者http之类的方式去调用后运行,但是这个功能交由系统级别来实现了。
第二章主要是介绍了远程服务调用(RPC)以及REST风格的设计。这个章节介绍了RPC的历史,概念有很多,毕竟RPC的历史也有几十年了。这个我理解需要了解的主要是知道现在一些主流的RPC吧,比如RMI(SUN/Oracle)、gRPC(Google)、Motan1/2(新浪)、Finagle(Twitter)、brpc(百度/Apache)、NetRemoting(微软)、Arvo(Hadoop)、JSON-RPC2.0)(JSON-RPC)等一些常见的协议和框架。
RESTFUL概念:restful和RPC的概念不尽相同,只是有些相似。本质上不是一类东西。 RPC是一种远程服务调用协议,而restful没有协议。虽然都是远程调用,REST是面向资源的思想,REST只是一种风格,不是一种规范,没有像RPC一样的协议
以作者举例来说明:
去医院预约
如果只是通过RPC调用,属于0级
如果定义了资源,比如能获取指定日期的预约结果,即拿到所有医生的信息 是一级
如果除了预约,还能取消、更换时间、以及结果能够根据统一code码进行判断的,并且考虑授权之类的,比如vip才能预约的,则称为二级
如果请求了一个,能返回所有的,则称为三级
restful 按照服务接口 rest的程序 从高到低,分为3级:
0级: 完全不REST
1级: 引入资源的概念
2级:统一引入接口,映射到http协议的方法上
3级: 超媒体控制,主要体现在返回信息里面包含所有的需要的信息,能做到服务端和客户端的api解耦。
另外就是restful的不足: rest和http 完全绑定,不适合高性能传输的常见、不利于事务、缺乏对资源批量处理等
第三章 主要是事务处理,这个章节是一些互联网公司经常面试的东西。
ACID(原子性、隔离性、持久性、一致性):事务的基本概念
原子性和持久性:默认是通过commit log来保证,但是commit log 有一个先天缺陷: 对数据的修改都必须发生在事务提交后,如果磁盘i/o足够空间,都不允许事务提交前修改磁盘数据,导致对提升数据库性能不利。 一种解决方案是: 增加Undo log的日志类型,记录修改数据位置以及修改的值,以便在事务回滚或者崩溃crash恢复时候根据undo log 对写入数据进行擦除。还有就是是 steal 和 force的一些概念
隔离性:通过数据库的读锁、写锁、范围锁 来解决,针对隔离级别里面序列化、可重复读、读已提交和读未提交。另外在可重复读和读已提交 还有MVCC机制来进行优化的场景,通过增加版本号的概念来进行。
全局事务: 为了解决分布式事务一致性问题,引入XA的处理事务架构。引入了全局的事务管理器,俗称2阶段提交和3阶段提交。
分布式事务:当前业界主流的。首先是CAP理论:即在分区容忍性下面,一致性(Consistency)和 可用性(Availability)只能保证一个。 针对分布式事务,有几种常用的方案:
队列:通过队列的方式,达到最终一致性
TCC: tyr-confirm-console,一般用的比较多的场景就是解决秒杀保证库位不会出现小于0的问题,超售的问题。缺点是 代码侵入强
SAGA事务:可以参考阿里开源的seata
第四章主要是缓存、域名解析、路由以及CDN分发、负载均衡的一些知识
主要是记一下CDN以及负载均衡的一些知识:
CDN 作用: 加速静态资源分发、安全防御、 协议升级(使用SSL证书)、状态缓存、修改资源、访问控制、诸如功能
负载均衡可以在数据链路做负载均衡、也可以在网络层和应用层做负载均衡,负载均衡算法可以使用加权、轮训、随机、一致性hash、最少连接数等方式
缓存的一些知识点:
吞吐量:使用OPS(每秒操作数)来衡量,反应了对缓存进行并发度、写操作的效率。
命中率: 成功从缓存返回结果次数与总请求次数的比值。
介绍了 Caffeine环形缓存(Ring buffer)的实现原理:以及缓存的淘汰策略:FIFO(优先淘汰)、LRU(优先淘汰最久未被访问的数据)、LFU(优先淘汰最不经常使用的数据)。另外就是介绍了一下缓存的一些风险以及应对方式:
缓存穿透: 查询的数据在缓存以及数据库都不存在。导致每次查询都取库表查询,如果流量过大,就起不到缓存DB压力的作用了。解决方案有两种:如果是业务逻辑本身不能避免的,可以约定再一定时间内对返回为空的key值就行缓存,也就是把该key值放入缓存,只是值为空;如果是恶意的共计导致缓存穿透,可以设置一个布隆过滤器:在查询缓存之前,先去布隆过滤器判断下,如果没有,缓存都不需要查询。
缓存击穿:查询的数据过期或者其他原因失效了,此时又有多个请求过来,导致每次查询都取库表查询,这种被称为缓存击穿。解决方案一种是加锁同步,另一种是如果是热点数据,可以通过程序手动完成更新操作,比如定时、查询后再次写入缓存
缓存雪崩:缓存击穿是针对单个热点失效,而缓存雪崩则是针对大量的热点缓存数据失效。解决方案包括但不陷入:使用分布式缓存、缓存时间由固定周期改为随机时间等
缓存污染:只要是指缓存中的数据和真实数据源中的数据不一致的现象。解决方案一般是写数据时候,先更新库表,在更细缓存,读数据时候,先读缓存,没有在读库表。
第五章主要是安全方面的,包括认证、授权、凭证、保密、传输这些。之前搞过shiro相关的,这部分没有细看。跳过
第六章是分布式相关的,包括分布式共识
Paxos算法- 主要是基于选主算法,包括常用的zk的选主机制。主要思想就是当主节点挂掉后,从节点进行自动选主,最终超过一般就算选主成功。
Multi Paxos: 对Paxos算法的改进,主要是选主的改进。Raft、ZAB算法都类似,是Multi Paxos的等价派生
第七章主要是类库到服务,围绕服务发现、网关路由以及负载均衡来展开叙述。
服务调用由全限定名、端口号、服务标识构成,由DNS负责解析全限定名到具体的ip地址,由负载均衡器负责到具体的路由。比较成熟的比如spring cloud的 eureka、HashiCorp的Consul以及阿里巴巴的nacos等。
其中eureka是优先保证服务的高可用性,底层通过ribbon和Hystrix模块来实现。 Consul 是优先保证可靠性,采用Raft算法来实现。nacos是采用自研的Distro协议实现的AP优先保证高可用,同时也支持高可靠,根据配置。
网关在分布式环境主要是做路由器以及过滤器的,比如springcloud的zuul 2.0 ,网关是所有服务对外的总出口,所以网关也影响全局的性能。网关的性能与它的工作模式和自身实现算法都有关系,而工作模式是最关键的。常用的代理模式有DSR三角传输模式(请求经过负载均衡器,服务相应无需从负载均衡器原路返回,整个请求、转发、相应的链路形成一个三角关系)、代理模式。默认的都是代码模式,因为服务网关默认支持七层路由,默认无法直接进行流量转发。而代理模式的性能则主要取决于如何代理网络请求,也就是网络的I/O模型。
网络IO模型划分为 两类、五中模型。两类:同步I/O,异步I/O,五种是指同步IO中又划分为 阻塞IO、非阻塞IO、多路复用IO、信号驱动IO以及异步IO模型。
阻塞IO: 优点节省CPU资源,缺点是线程休眠所带来的上下文切换,需要切换到内核态的重负载操作.
非阻塞IO:非阻塞IO能避免线程休眠,可以节省上下文切换的消耗、,但是对于较长时间返回的请求,白白浪费了CPU资源
多路复用IO:本质上是阻塞IO一种,但是好处是可以在同一条阻塞线程上处理多个不同端口号的监听。多路复用是目前高并发网路的主流,还提供了selcet/epoll/kqueue等不同实现。(区别:select有最大文件描述符的限制1024,epoll优化了select和poll的性能瓶颈使用事件驱动模式,kqueue是BSD系统提供的多路复用)
负载均衡包括客户端负载均衡、集中式负载均衡(服务端负载均衡)、代理负载均衡
集中式负载均衡:部署在服务器前端,负载用户 请求分流到各个服务处理,在微服务里面 又称为 服务端负载均衡
客户端负载均衡:微服务中和服务实例一一对应的,具体实现比如spring cloud 的 LoadBalancer 和 Netflix Ribbon
代理负载均衡:微服务时代 服务网格提出后一种新型的的负载均衡。
第八章 服务治理 ,主要是对服务容错和流量控制的介绍
服务容错策略: 断路器模式、舱壁隔离模式、重试模式
断路器模式:通过代理接管服务调用的请求,通过监控并统计服务返回的超时、失败、拒绝、成功等结果,出现故障或者达到阈值时候,状态进行变更。达到避免雪崩的目的
舱壁隔离模式:为服务单独设立线程池,或者使用信号量机制(Semaphore)的机制
重试模式:故障转移和故障恢复策略对服务进行重试,当一个机器请求异常时候则请求其他的机器
流量统计,有几个指标:
TPS:每秒事务请求数,衡量系统吞吐量的标准
HPS:每秒请求数,每秒从客户端向服务端请求的次数
QPS:每秒查询数,一台机器能相应的查询数
限流主流的是使用HPS作为限流指标。
针对限流 有几种常用的方案:滑动窗口、流量计数器、令牌桶和漏桶算法
滑动窗口:类似滑动窗口算法
令牌桶:每秒固定向系统添加令牌,当系统空闲时候则会达到最大值。有请求进来则从中领取一个令牌。令牌为0 则达到请求上限。
漏桶算法:一个请求元素作为对象的FIFO队列,队列满时候 则拒绝请求
第九章关于认证、授权相关的 没有详细去看
第十章 可观测性,主要是基于 监控相关的。
首先是基于elk(elasticsearch/logstash/kibana)的日志收集的一些介绍,这些一般公司都有使用;之后介绍了一些链路追踪的知识,比如基于traceId的链路追踪,基于span的监控。一些常用的监控追踪工具,比如zipkin/skywalking/pinpoint等,还有基于边车代理Envoy的追踪,这些追踪的图形化界面要不没有,要不不是很好,一般是借助grafana进行展示。还有就是现在主流的prometheus监控面板,prometheus内置了一个强大的时序数据库实现,时序数据库提供了PromQL数据查询语法,可以通过grafana 的模板进行展示。这个也是当前主流公司的一些选择。
第十一章 是虚拟化容器相关的
讲了docker的历史,以及kubernetes等概念。以及一些以应用为中心的系统。
容器最初的摸底不是部署软件,而是隔离计算机的各类资源,以便降低软件开发,隔离文件 的是chroot,隔离访问:linux的名称空间(linux的名称空间由内核直接提供全局资源封装,最初只是为了隔离文件),隔离资源的cgroup(控制群组,与名称空间一样,直接由内核提供,用户隔离或者分配限制某个进程组能够使用的资源配额,包括处理器时间、内存大小、磁盘i/o速度等)。文件隔离、访问隔离、资源隔离这些满足了容器降生所需要的条件,刚开始linux提供的是 linx Container(后面检测LXC)提供系统级的虚拟化功能,但是LXC是基于系统而不是基于应用的,而这也为接下来docker的产生提供了条件。
docker以应用为中心来提供封装,支持夸机器部署,自动构建、多版本支持,组件重用以及共享等功能,也为接下来容器编排提供了铺垫。kubernetes是以集群为封装,在kubernetes里面 ,资源和控制器是两个核心设计理念。kubernetes里面认为一切皆是资源,比如pod、volumn,当这些资源状态变化时候,需要由控制器进行追踪,监控资源的使用。
针对kubernetes以及docker的维护、部署和应用困难的问题,还需要一个载体去封装整个应用。比如使用配置文件来配置配置文件,或者像linux一样管理。场景封装应用的方案,比如kustomizi/helm/chart/operator/CRD以及阿里开发应用模型等。
十二章 容器间网络
主要是介绍虚拟化网络通信、容器通信以及容器网络相关的。
首先是介绍了下网络通信模型,七层模型以及四层模型,应用层、传输层、网络层以及网络访问层。平时开发主要是在应用层开发,应用层一般也就是用户空间,下面的几层成为内核空间,用户空间和内核空间通过socker进行网络通信。在网络协议层,linux防火墙的作者 围绕网络层(IP协议层)埋下了五个钩子,使得数据包可以流到网络层。因为应用层到下面的一些socket信息,可以拿到后,我们就可以对这些socket信息信息更改。比如更改目标的ip地址、封包、宽带限速等等。
虚拟网卡不需要遵照物理网络的样子来设计,我理解虚拟网卡只是在网络协议层,拿到网络协议层的socker信息后进行了更改,比如更改了host地址,这样就能让其指向我们想要发送的主机上面了。网络间的通信,我理解也是类似。这部门设计一些网络相关的知识,包括下面的VLAN模式、MACVLAN模式,不是很理解。
十三章和十四章 是容器的存储和资源的调用