微服务架构下的高可用和高性能设计

时间:2021-10-22 06:56:16

今天再谈下微服务架构下的高可用性设计。

微服务架构下的高可用和高性能设计

对于高可用性实际应该包括了高可靠性,高性能和高扩展性。因此谈微服务架构的高可用性,首先需要梳理三者之间的关系。

高可用性三个维度和相互关系

 

对于业务系统的高可用性,实际上包括了高可靠,高性能和高扩展三个方面的内容。而且三方面相互之间还存在相互的依赖和影响关系。

对于三者的关系,我们可以用下图进行描述。

微服务架构下的高可用和高性能设计

上图可以看到高可靠,高性能和高扩展性三者之间的关系。

对于高可靠性来来说,传统的HA架构,冗余设计都可以满足高可靠性要求,但是并不代表系统具备了高性能和可扩展性能力。反过来说,当系统具备了高扩展性的时候,一般我们在设计扩展性的时候都会考虑到同时兼顾冗余和高可靠,比如我们常说的集群技术。

对于高性能和高扩展性两点来说,高扩展性是高性能的必要条件,但是并不是充分条件。一个业务系统的高性能不是简单的具备扩展能力就可以,而是需要业务系统本身软件架构设计,代码编写各方面都满足高性能设计要求。

对于高可靠和高性能,两者反而表现出来一种相互制约的关系,即在高性能支撑的状态下,往往是对系统的高可靠性形成严峻挑战,也正是这个原因我们会看到类似限流熔断,SLA服务降级等各种措施来控制异常状态下的大并发访问和调用。

数据库的高可用性

 

在我前面谈微服务架构的时候就谈到,在微服务架构下传统的单体应用要进行拆分,这个拆分不仅仅是应用层组件的拆分,还包括了数据库本身的拆分。

如果一个传统的单体应用规划为10个微服务,则可能会垂直拆分为10个独立的数据库。这实际上减小了每一个数据库本身面对的性能负荷,同时提升了数据库整体的处理能力。

同时在拆分后虽然引入了各种跨库查询,分布式事务等问题,但是实际很多跨库操作,复制的数据处理计算都不在数据库里面完成,数据库更多的是提供单纯的CRUD类操作接口,这本身也是提升数据库性能的一个关键。

如果采用Mysql数据库。

要满足高可靠性,你可以采用Dual-Master双主架构,即两个主节点双活,但是仅一个节点提供数据库接口能力,另外一个节点实时同步数据库日志,作为备节点。当主节点出现故障的时候,备节点自动转变为主节点服务。

简单的双主架构两节点间安装代理,通过Binlog日志复制,上层通过类似Haproxy+Keepalive实现通过的VIP浮动IP提供和心跳监测。

可以看到双主架构更多的是为高可靠服务。

如果要满足高性能,常采用的是读写分离集群。即1个主节点承担读写操作,多个从节点承担读操作。从节点仍然是通过Binlog日志进行主节点信息同步。当有数据访问请求进入的时候,前端Proxy可以自动分析是CUD类请求,还是R读请求,以进行请求的路由转发。

当我们进行订单新增操作的时候,当新增成功的时候需要快速的刷新当前订单列表界面,第二次的刷新本身是读操作,但是和前面的写绑定很紧,实际上不太适合从Slave节点读取数据的。这个时候可以在进行Sql调用的时候明确指定是否仍然从主节点获取数据。

当然,大部分时候可能需要两者结合,既提供足够的高可靠性,又提供足够的高性能。因此Mysql集群在搭建的时候既需要进行双主设置,又需要进行多个从节点设置。

微服务架构下的高可用和高性能设计

在上图这种逻辑部署架构下,基本就可以同时满足高可靠和高性能两个方面的需求。但是从上面架构部署可以看到,备节点的主和从都处于一种热备无法实际提供能力状态。

是否可以将所有Slave挂到一个Master上?

如果这样设计,那么当主Master出现故障的时候,就需要对多个Slave节点进行自动化漂移。这一方面是整体实现比较复杂,另外就是可靠性也不如上面这种架构。

对数据库性能扩展的思考

 

首先来看前面架构本身的一些潜在问题点:

第一就是CUD操作仍然是单节点提供能力。对于读操作占大部分场景的,基本可以通过双主+读写分离集群实现很好的性能扩展。但是如果CUD操作频繁仍然可能出现性能问题。

其次,数据库性能问题一般分为两个层面,其一就是大并发请求下的性能,这个可以通过集群负载均衡去解决,其二是单个请求访问大数据库表模糊查询性能,这个是服务通过负载去解决的。

也就是说上面的设计,在大并发的CUD操作,对大数据表的关联查询或模糊查询操作仍然可能出现明显的性能问题。

如何来解决这个问题?

微服务架构下的高可用和高性能设计

简单来说就是写入通过消息中间件来将同步转异步,进行前端削峰。而对于查询则进行内容缓存或创建二级索引,提升查询效率。

对于查询本身又包括了偏结构化数据查询和处理,类似采用Redis库或Memcached进行缓存;而对于非结构化数据,类似消息报文,日志等采用Solr或ElasticSearch构建二级索引并实现全文检索能力。

当面临大量的数据写入操作类操作的时候,单个Master节点往往性能很难支撑住,这个时候采用类似RabbitMQ,RocketMQ,Kafka等消息中间件来进行异步销峰处理就是必要的。这个异步实际上涉及到两个层面的异步。

其一是对于发短信,记录日志,启流程等接口服务异步。其二是对长耗时写入操作异步,先反馈用户请求收到,处理完再通知用户拿结果。

而对于查询操作,前面谈到的并发查询可以进行集群负载。

但是对于大数据量表,比如上亿记录的大表模糊查询,这块就必须进行二级索引。对这种大的数据表的查询即使没有并发查询,如果不进行二级索引,查询效率和响应速度仍然很慢。

对半结构化信息启用分布式存储

微服务架构下的高可用和高性能设计

对于类似日志,接口服务调用日志等半结构化信息,本身数据量很大,如果全部存储在结构化数据库中,那么对存储空间需求很大,而且很难扩展。特别是前面的Mysql集群方案本身还是采用本地磁盘进行存储的情况下。

因此需要对历史日志进行清除,同时将历史日志迁移到分布式存储库,比如Hdfs或Hbase库,然后基于分布式存储再构建二级缓存能力。

构建DaaS数据层进行水平扩展

微服务架构下的高可用和高性能设计

前面谈到在拆分了微服务后已经进行了垂直扩展,比如一个资产管理系统可以拆分为资产新增,资产调拨,资产折旧,资产盘点等10个微服务模块。

但是在拆分后仍然发现资产数据量极大,比如在集团集中化这种大型项目可以看到,一个省的资产数据表就接近上亿条记录。这种时候将所有省数据全部集中化在一个数据库管理不现实。因此需要进一步按省份或组织域进行水平拆分。

在水平拆分后,在上层构建DaaS层提供统一对外访问能力。

应用集群扩展

 

对于应用集群扩展实际比数据库层要简单,应用中间件层可以很方便的结合集群管理节点或者独立的负载均衡硬件或软件进行集群能力扩展。对于应用集群扩展,本身就是提升整个性能的关键方式。在集群的扩展过程中还是有些问题需要进一步讨论。

集群做到完全的无状态化

如果集群做到完全的无状态化,那么集群就可以做到和负载均衡设备或软件结合来实现负载均衡扩展能力。比如硬件常用的F5或radware等,软件如HAProxy,Nginx等。

Session会话信息如何处理?对于Session本身是有状态的,因此对于Session信息可以考虑存储到数据库或Redis缓存库中。

集群节点在启动的时候往往需要读取一些全局变量或配置文件信息,这些信息如果简单的存在在本地磁盘往往难以集中化管理。因此当前主流思路是启用全局的配置中心来统一管理配置。

如果应用功能实现中存在文件的上传和存储,那么这些文件存储在磁盘本地本身也是有状态的,因此对于这些文件本身也需要通过文件服务能力或分布式对象存储服务能力来实现。

微服务架构下各个微服务间本身存在接口交互和协同,对于接口调用的具体地址信息也需要通过服务注册中心获取,获取后可以缓存在本地,但是必须有变更后实时更新机制。

四层负载和七层负载

微服务架构下的高可用和高性能设计

首先看下最简单的四层负载和七层负载的一个说明:

  • 四层负载:即在OSI第4层工作,就是TCP层,可以根据IP+端口进行负载均衡。此种Load Balance不理解应用协议(如HTTP/FTP/MySQL等等)。
  • 七层负载:工作在OSI的最高层,应用层,可以基于Http协议和URL内容进行负载均衡。此时负载均衡能理解应用协议。

当前可以看到对于F5,Array等硬件负载均衡设备本身也是支持7层负载均衡的,同时在四层负载均衡的时候我们还可以设置是否进行会话保持等高级特性。要明白四层负载均衡本质是转发,而七层负载本质是内容交换和代理。

也就是说在不需要进行状态保留和基于内容的路由的时候,我们完全可以启用四层负载均衡来获取更好的性能。

在微服务架构前后端分离开发后。

后端微服务组件可以完全提供Rest API接口服务能力,那么本身就无状态。而对于前端微服务组件直接面对最终用户访问,需要保持Session状态。在这种情况下就可以进行两层负载均衡设计,即前端采用七层负载,而后端采用四层负载均衡。

前端缓存

前端缓存主要是分为HTTP缓存和浏览器缓存。其中HTTP缓存是在HTTP请求传输时用到的缓存,主要在服务器代码上设置;而浏览器缓存则主要由前端开发在前端js上进行设置。缓存可以说是性能优化中简单高效的一种优化方式了。一个优秀的缓存策略可以缩短网页请求资源的距离,减少延迟,并且由于缓存文件可以重复利用,还可以减少带宽,降低网络负荷。

具体可以参考:

https://www.jianshu.com/p/256d0873c398

软件性能问题分析和诊断

 

微服务架构下的高可用和高性能设计

对于业务系统性能诊断,如果从静态角度我们可以考虑从以下三个方面进行分类

  1. 操作系统和存储层面
  2. 中间件层面(包括了数据库,应用服务器中间件)
  3. 软件层面(包括了数据库SQL和存储过程,逻辑层,前端展现层等)

那么一个业务系统应用功能出现问题了,我们当然也可以从动态层面来看实际一个应用请求从调用开始究竟经过了哪些代码和硬件基础设施,通过分段方法来定位和查询问题。

比如我们常见的就是一个查询功能如果出现问题了,首先就是找到这个查询功能对应的SQL语句在后台查询是否很慢,如果这个SQL本身就慢,那么就要优化优化SQL语句。如果SQL本身快但是查询慢,那就要看下是否是前端性能问题或者集群问题等。

软件代码的问题往往是最不能忽视的一个性能问题点

对于业务系统性能问题,我们经常想到的就是要扩展数据库的硬件性能,比如扩展CPU和内存,扩展集群,但是实际上可以看到很多应用的性能问题并不是硬件性能导致的,而是由于软件代码性能引起的。对于软件代码常见的性能问题我在以往的博客文章里面也谈过到,比较典型的包括了。

  1. 循环中初始化大的结构对象,数据库连接等
  2. 资源不释放导致的内存泄露等
  3. 没有基于场景需求来适度通过缓存等方式提升性能
  4. 长周期事务处理耗费资源
  5. 处理某一个业务场景或问题的时候,没有选择最优的数据结构或算法

以上都是常见的一些软件代码性能问题点,而这些往往需要通过我们进行Code Review或代码评审的方式才能够发现出来。因此如果要做全面的性能优化,对于软件代码的性能问题排查是必须的。

其次就是可以通过APM性能监控工具来发现性能问题。

传统模式下,当出现CPU或内存满负荷的时候,如果要查找到具体是哪个应用,哪个进程或者具体哪个业务功能,哪个sql语句导致的往往并不是容易的事情。在实际的性能问题优化中往往也需要做大量的日志分析和问题定位,最终才可能找到问题点。

而通过APM可以很好的解决这个问题。

比如在我们最近的项目实施中,结合APM和服务链监控,我们可以快速的发现究竟是哪个服务调用出现了性能问题,或者快速的定位出哪个SQL语句有验证的性能问题。这个都可以帮助我们快速的进行性能问题分析和诊断。

资源上承载的是应用,应用本身又包括了数据库和应用中间件容器,同时也包括了前端;在应用之上则是对应到具体的业务功能。因此APM一个核心就是要将资源-》应用-》功能之间进行整合分析和衔接。通过APM来发现应用运行中的性能问题并解决。

原文链接:https://www.toutiao.com/a6903759514290405899/