1、需求来源
显然,架构要求高可用,那么为什么系统会出现单点呢?
答:单点主控的设计会大大简化系统设计,即使有时单点无法避免
什么场景下会出现单点? 我们来看一个典型的互联网高可用架构。
典型的互联网高可用架构:
(1)客户端层,这一层是浏览器或者APP,第一步是访问DNS-服务器,从域名中获取nginx的内部IP
(2)负载均衡层,nginx是整个服务器的入口,负责反向代理和负载均衡
(3)站点层,Web服务器层,通常是tomcat或apache
(4)Service层,服务层,一般是dubbo或者thrift提供RPC调用的前端服务
(5)数据层,包括cache和db,一般是主从复制、读写分离的db架构
在这个互联网架构中,站点层、服务层、数据库从库都可以通过冗余的方式保证高可用,但至少
(1)nginx层是潜在的单点
(2)数据库写数据库master也是潜在的单点
再举一个GFS(GoogleFileSystem)框架的例子。
GFS的系统架构中主要有以下几个角色:
(1)客户端,即发起文件读写的调用端
(2)master,这是一个单点服务,它有全局业务,掌握文件元信息
(3) chunk-server,实际存储文件的服务器
在这个系统中,master也是单点服务,Map-reduce系统也有类似全局协调master单点的角色。
在系统架构设计中,nginx、db-master、gfs-master等单点服务会存在哪些问题,有哪些解决方案来优化? 这些都是本文要讨论的问题。
2、单点架构的问题
单点系统一般有两大问题:
(1)非高可用:由于是单点,一旦master出现故障,服务就会受到影响
(2)性能困境:由于是单点,不具备良好的扩展性,所以服务性能总是存在上限。 这个单点的性能上限往往就是整个系统性能的上限
拿出来,我们看看有什么优化方法可以优化上面提到的两个问题
3、Shadow-master解决单点高可用问题
Shadow-master是解决单点高可用问题非常常见的技术方案。
“影子主控”,顾名思义,当服务正常时,它只是单点主控的一个影子。 当master出现故障时,shadow-master会手动成为master并继续提供服务。
Shadow-master可以解决高可用的问题,并且故障转移是手动的,无需人工干预,但缺点是使服务资源的利用率降低到50%。 业界经常采用keepalived+vip的形式来实现这种单点高可用。
以GFS master为例,当master正常时:
(1) 客户端会连接普通master,shadow-master不会对外提供服务
(2)master和shadow-master之间有生存检测机制
(3)master和shadow-master具有相同的虚拟IP(virtual-IP)
当发现master异常时:
Shadow-Master会手动成为Master,虚拟IP机制可以保证这个过程对于调用者来说是透明的
不仅是GFS和MapReduce系统中的主控master,nginx也可以使用类似的方法来保证高可用,数据库的主数据库master(主库)也可以使用类似的方法来保证高可用,但是有有一些需要注意的细节:
传统读写分离的一主多从DB架构只能保证读库的高可用,而很难保证写库的高可用。 为了保证写库的高可用,还可以使用上面提到的shadow-master机制:
(1)双主模式,即两个主库设置为相互同步
(2)一般情况下,只有一个主图书馆提供服务。 含义是shadow-master不会向master同步数据
(3)异常时,虚拟IP漂移到另一个主库软件高可用性,shadow-master成为主库继续提供服务
需要注意的是,由于数据库的特殊性,数据同步需要信噪比。 如果数据尚未同步,流量会切换到shadow-master上,可能会造成少量数据不一致。
4、减少单点交互是单点系统优化的核心方向
既然我们知道单点的性能是有上限的,那么单点(比如GFS中的master)的性能可能会成为系统的困境。 因此,减少与单点的交互成为单点系统优化的核心方向。
如何简化与单点的交互,这里介绍两种常用的技巧。
批量写入
批量写入是提高单点性能的常用方法。
例如使用数据库编写单点生成“ID生成器”的反例:
(1)业务方需要身份证件
(2)使用数据库写单点autoincreamentid后世并返回ID
这是很常见的情况,很多公司都是通过这种方式生成ID的。 它利用了在数据库中写入单点的特性,方便快捷,并且没有额外的开发成本。 这是一个非常可爱的解决方案。
潜在的问题是:ID生成的并发上限取决于单点数据库的写入性能上限。
如何提高性能?批量写入
(1)中间添加一个service,每次从数据库拉取100个id
(2)业务方需要身份证件
(3)服务直接返回100个id中的一个,100个全部分配完后,访问数据库
这样每分配100次就会写入一次数据库,分配id的性能可以认为提高了100倍。
客户端缓存
客户端缓存也是减少单点交互次数、提高系统整体性能的一种方式。
还是以GFS文件系统为例:
(1)GFS调用client客户端访问shenjian.txt,首先查询本地缓存,miss
(2)客户端访问master询问文件在不在,master告诉客户端在chunk3上
(3)客户端将shenjian.txt存储在chunk3中并记录到本地缓存中,然后读写该文件
(4)以后如果客户端要访问该文件软件高可用性,从本地缓存中找到对应的记录,就可以直接访问chunk-server,而不需要请求master。 如果文件被传输,chunk3返回给客户端说“文件不再在我这里了”,然后客户端访问master来询问服务器文件所在的位置。
根据经验,这种类型的缓存命中率非常高,可能达到99.9%以上(因为手动迁移文件的概率很小),因此与master的交互次数增加了1000倍。
5、水平扩展是提高单点系统性能的好方案
无论批量写入、客户端缓存,即使单点是单机,仍然存在性能限制。
理论上,只有想办法横向扩展,消除系统中的单点,才能无限完善系统。
以nginx为例,如何水平扩展?
第一步DNS解析只能返回一个nginx内网IP吗? 答案其实是否定的。 “DNS协程”技术支持DNS-server返回不同的nginx内网IP,从而可以实现nginx负载均衡层的水平扩展。
对于DNS-server部分,一个域名可以配置多个IP。 每次进行DNS解析请求时,协程都会返回不同的IP,从而可以实现nginx的水平扩展,扩展负载均衡层的整体性能。
数据库单点写入也是如此。 在数据量较大的情况下,可以采用水平分割的方式来提高写入性能。
遗憾的是,并不是所有的业务场景都可以水平拆分。 比如秒杀业务中,条目数量可能不会很多,数据库的数据量也不大,因此无法通过水平拆分来提升秒杀系统的整体写入性能(不可能一个库中有 100 条记录,对吧?)。
六、总结
明天的话题讨论到此结束。 内容较多,占用您宝贵的时间,敬请谅解。 恐怕我不记得其中的大部分了。 至少记住以下几点:
(1)单点系统中的问题:可用性问题、性能困境
(2)shadow-master是单点系统可用性问题的常用解决方案
(3)减少与单点的交互是系统单点优化的核心方向。 常见的方法有批量写入和客户端缓存
(4)水平扩展也是提高单点系统性能的一个很好的解决方案
如果您有什么收获,请帮忙转发。
==【完】==
返回【搜索】
返回【钱包】
返回【留言】
返回【秒杀】
返回【设置】
回来[延迟]
返回【推荐】
【小游戏:返回小于10的整数,随机返回一篇好文章,猜猜怎么实现】
欢迎讨论,解答各种问题。