隔离

什么是隔离?

隔离,本质上是对系统或资源进行分割,从而实现当系统发生故障时能限定传播范围和影响范围,即发生故障后只有出问题的服务不可用,保证其他服务仍然可用。

服务隔离

动静隔离

例如 CDN

小到 CPU 的 cacheline false sharing、数据库 mysql 表设计中避免 bufferpool 频繁过期,隔离动静表,大到架构设计中的图片、静态资源等缓存加速。本质上都体现的一样的思路,即加速/缓存访问变换频次小的。比如 CDN 场景中,将静态资源和动态 API 分离,也是体现了隔离的思路:

  • 降低应用服务器负载,静态文件访问负载全部通过CDN。
  • 对象存储存储费用最低。
  • 海量存储空间,无需考虑存储架构升级。
  • 静态CDN带宽加速,延迟低。

archive: 稿件表,存储稿件的名称、作者、分类、tag、状态等信息,表示稿件的基本信息。 在一个投稿流程中,一旦稿件创建改动的频率比较低。 archive_stat: 稿件统计表,表示稿件的播放、点赞、收藏、投币数量,比较高频的更新。 随着稿件获取流量,稿件被用户所消费,各类计数信息更新比较频繁。 MySQL BufferPool 是用于缓存 DataPage 的,DataPage 可以理解为缓存了表的行,那么如果频繁更新 DataPage 不断会置换,会导致命中率下降的问题,所以我们在表设计中,仍然可以沿用类似的思路,其主表基本更新,在上游 Cache 未命中,透穿到 MySQL,仍然有 BufferPool 的缓存。

读写隔离

例如主从,除此之外还有常见的 CQRS 模式,分库分表等

常见的隔离技术,当用于读取操作的服务器出现故障时,写服务器照常可以运作,反之也一样。

轻重隔离

  • 核心隔离:例如上面讲到将核心业务独立部署,非核心业务共享资源
  • 热点隔离:例如上面讲到的 remote cache 到 local cache
  • 用户隔离:不同的用户可能有不同的级别,例如上面讲到的外部用户和管理员

物理隔离

线程

常见的例子就是线程池,这个在 Golang 中一般不用过多考虑,runtime 已经帮我们管理好了

主要通过线程池进行隔离,也是实现服务隔离的基础。(可将图中隔离媒介换成线程池即可)

把业务进行分类并交给不同的线程池进行处理,当某个线程池处理一种业务请求发生问题时,不会讲故障扩散和影响到其他线程池,保证服务可用。

假设系统存在商品服务、用户服务和订单服务3个微服务,通过设置运行时环境得到3个服务一共使用200个线程,客户端调用这3个微服务共享线程池时可能会引发服务雪崩,将线程分别隔离后则不会触发整体雪崩。

进程

我们现在一般使用容器化服务,跑在 k8s 上这就是一种进程级别的隔离

将系统拆分为多个子系统来实现物理隔离,各个子系统运行在独立的容器和JVM中,通过进程隔离使得一个子系统出现问题不会影响其他子系统。

机房

我们目前在 K8s 的基础上做一些开发,常见的一种做法就是将我们的服务的不同副本尽量的分配在不同的可用区,实际上就是云厂商的不同机房,避免机房停电或者着火之类的影响

如果有条件,对于大型高可用系统,会进行多机房部署,每个机房的服务都有自己的服务分组,本机房的服务应该只调用同机房服务。

当一个机房出现故障,将请求快速切换到其他机房确保服务继续可用。

集群

非常重要的服务我们可以部署多套,在物理上进行隔离,常见的有异地部署,也可能就部署在同一个区域

将某些服务单独部署成集群,或对于某些服务可以进行分组集群管理,某一个集群出现问题之后就不会影响到其他集群,从而实现隔离。

参考文章