高并发系统设计要点

news2024/11/25 2:43:08

在系统设计时,如果能预先看到一些问题,并在设计层面提前解决,就会给后期的开发带来很大的便捷。相反,有缺陷的架构设计可能会导致后期的开发工作十分艰难,甚至会造成“推倒重来”的情形。因此,在系统设计阶段,应该尽可能的规避项目开发中可能会遇到的各种问题。本文就选取了几个经典的问题进行介绍。

Session共享问题
在Web项目中,Session是服务端用于保存客户端信息的重要对象。单系统中的Session对象可以直接保存在内存中,但在分布式或集群环境下,多个不同的节点就要采取措施来共享Session对象,具体可以使用以下几种方式。

1.Session Replication

SessionReplication是指在客户端第一次发出请求后,处理该请求的服务端就会创建一个与之对应的Session对象,用于保存客户端的状态信息,之后为了让其他服务端也能保存一份此Session对象,就需要将此Session对象在各个服务端节点之间进行同步,如图1所示。

图1 Session Replication

SessionReplication的这种Session同步机制虽然能够使所有的服务节点都拥有一份Session对象,但缺点也是很明显的,如下所示。

(1)容易引起广播风暴。试想,如果有50个服务节点,那么当一个节点产生了Session对象后,就需要将该Session对象同步到其他49个节点中,因此会增大网络的开销。

(2)会造成严重的冗余。如果有多个用户同时在访问,那么每个服务节点中都会保存多个用户的Session对象。可以发现,服务节点的个数越多,Session冗余的问题就越严重。因此Session Replication方式只适用于服务节点较少的场景。

2.Session Sticky

Session Sticky是通过Nginx等负载均衡工具对各个用户进行标记(例如对Cookie标记),使每个用户在经过负载均衡工具后都请求固定的服务节点,如图2所示。

图2 Session Sticky

客户端A的所有请求都被Nginx转发到了应用服务X上,客户端B的所有请求都被转发到了应用服务Y上,因此各个服务中的Session就无需同步。但此种做法也有严重的弊端:如果某个服务节点宕机,那么该节点上的所有Session对象都会丢失。

3.独立Session服务器

除了Session Replication和Session Sticky两种方式以外,还可以将系统中所有的Session对象都存放到一个独立的Session服务中,之后各个应用服务再分别从这个Session服务中获取需要的Session对象,如图3所示。在大规模分布式系统中,就推荐使用这种独立Session服务方式。并且这种方式在存储Session对象时,既可以用数据库,也可以使用各种分布式或集群存储系统。

图3 独立Session服务器

优先考虑无状态服务

在使用了“独立Session服务器”之后,应用服务就是一种“无状态服务”,换句话说,此时的应用服务与用户的状态是无关的。例如,无论是哪个用户在什么时间发出的请求,所有的应用服务都会进行完全相同的处理:先从Session服务中获取Session对象,再进行相同的业务处理。

读者也可以从“有状态服务”来对比理解“无状态服务”。“有状态服务”是指不同的应用服务与用户的状态有着密切的关系。例如,假设应用服务A中保存着用户Session,应用服务B中没有保存。之后,如果用户发出一个请求,经过ngnix转发到了应用服务A中,那么就可以直接进行下单、结算等业务;而如果用户的请求被ngnix转发到了应用服务B中,就会提示用户“请先登陆……”。类似这种,不同应用服务因为对用户状态的持有情况不同,从而导致的执行方式不同,就可以理解为“有状态服务”。

总的来讲,“无状态服务”有很多优势,如下所示。

1.数据同步。

“有状态服务”为了在不同的服务节点之间共享数据,必然会进行数据同步,而不同节点之间的数据同步又会带来CPU/内存损耗、网络延迟、数据冗余等问题;而“无状态服务”不需要数据同步;

2.快速部署。

如果系统的压力太大,就需要增加新的服务节点,从而对系统扩容。如果采用的是“有状态服务”,就需要在扩容后先将其他服务节点上的Session等信息同步到新节点上;而如果采用的是“无状态服务”,就不必同步。可以发现,快速部署的优势本质上也归功于“数据同步”。

实际上,我们这里介绍的“状态”不仅仅是Session,也可以是任意类型的数据、结构化数据、非结构化数据、文件等。因此,要想真正的实现应用服务的“无状态”,就需要先将各种类型的“状态”各自集中存储,具体存储方式如下。

(1)Session等保存在内存中的数据:构建独立的服务,如Session服务。

(2)结构化数据:MySQL等关系型数据库。

(3)非结构化数据:

·适合存储非结构化数据的数据库,如HBase等;

·如果是海量的“状态”,也可以存储在ElasticSearch等搜索引擎中。

(4)文件:

·独立文件服务器,如HDFS、FastDFS等

·CDN。

以上的对应关系,如图4所示。

图4 “状态”的存储方式

明确了各个“状态”的存储方式后,我们就可以搭建出一套可快速扩容的系统:将系统中的应用服务设置为“无状态”,并注册到Eureka或ZooKeeper中。因为是“无状态”的服务,因此单个服务的宕机、重启等都不会影响到集群中的其他服务,并且很容易对应用服务进行横向扩展。另一方面,将带有数据的服务设置为“有状态”,并进行集群的“集中部署”(如MySQL集群),从而降低集群内部数据同步带来的延迟。

说明:“集中部署”是指尽可能的将相同或相关的数据、业务部署在同一机房中,利用内网提高数据的传输速度,尽量避免跨机房调用,如图5所示。

图5 多机房部署

在实际部署时,应该通过IP分组等方法尽量避免跨机房的数据传输、接口调用。并且使用DNS、Nginx等工具在某个机房整体故障时,将流量快速转接到其他机房。

最后要提醒大家的是,“无状态服务”虽然有很多的优势,但也不能盲目的将其作为唯一的选择。任何技术或架构的选择,都得看具体的业务场景,例如在小型项目或者仅有一个服务的项目中,就可以采用“有状态服务”来简化开发难度,缩短开发周期。

技术选型原则与数据库设计
在做技术选型时,既要注意待选技术的性能,也要考虑技术的安全性。并预估这些技术是否有足够长的生命力,项目组新成员是否能够快速掌握,而不能一味地追求技术的先进性。

这里以设计数据库为例,介绍一种数据库选型的思路。

以MySQL数据库为例,各种版本MySQL默认的并发连接数约为一二百,单机可配置的最大连接数为16384(一般情况下,由于计算机自身硬件的限制,单机实际能够负载的并发数最多为一千左右)。因此,高并发系统面临的最大性能瓶颈就是数据库。我们之前设计的各种缓存的目的,就是为了尽可能的减少对数据库的访问。

除了在页面、应用程序中增加缓存以外,我们还可以在应用程序和数据库之间加一层Redis高速缓存,从而提高数据的访问速度并且减少对数据库的访问次数,具体如下。

1.搭建高可用Redis集群,并通过主从同步进行数据备份、通过读写分离降低并发写操作的冲突、通过哨兵模式在Master挂掉之后选举新的Master;

2.搭建双Master的MySQL集群,并通过主从同步做数据备份;

3.通过MyCat对大容量的数据进行分库/分表,并控制MySQL的读写分离;

4.通过Haproxy搭建MyCat集群;

5.通过Keepalived搭建Haproxy集群,通过心跳机制防止单节点故障;并且Keepalived可以生成一个VIP,并用此VIP与Redis建立连接。

以上步骤如图6所示。

 

图6高性能高可用数据库架构

在实际进行数据库开发时,还需要合理使用索引技术以及适当设置数据库的各项性能参数,从而最大限度的优化数据访问操作。

缓存穿透与缓存雪崩问题
缓存可以在一定程度上缓解高并发造成的性能问题,但在一些特定场景下缓存自身也会带来一些问题,比较典型的就是缓存穿透与缓存雪崩问题。

注:为了讲解的方便,本文用MySQL代指所有的关系型数据库,用Redis代指所有数据库的缓存组件。

1.缓存穿透

缓存穿透是指大量查询一些数据库中不存在的数据,从而影响数据库的性能。例如Redis等KV存储结构的中间件可以作为MySQL等数据库的缓存组件,但如果某些数据没有被Redis缓存却被大量的查询,就会对MySQL带来巨大压力,如图7所示。

图7 缓存穿透

前面介绍过,单机MySQL最大能够承受的并发连接数只有一千左右,因此无论是设计失误(例如,某个高频访问的缓存对象过期)、恶意攻击(例如,频繁查询某个不存在的数据),还是偶然事件(例如,由于社会新闻导致某个热点的搜索量大增)等,都可能让MySQL遭受缓存穿透,从而宕机。

理解了缓存穿透的原因后,解决思路就已经明确了,举例如下。

(1)拦截非法的查询请求,仅将合理的请求发送给MySQL。如,可以使用验证码、IP限制等手段限制恶意攻击,并用敏感词过滤器等拦截不合理的非法查询。

(2)缓存空对象。如,假设在iphone9上市后,可能会导致大量用户搜索iphone9,但此时Redis和MySQL中还没有iphone9这个词。一种解决办法就是,将数据库中不存在的iphone9也缓存在Redis中,如Key=iphone9,value=””。之后,当用户再次搜索iphone9时,就可以直接从Redis中拿到结果,从而避免对MySQL的访问,如图8所示。

图8 缓存空对象

提示:为了减少Redis对大量空对象的缓存,可以适当减少空对象的过期时间。

(3)建立数据标识仓库。将MySQL中的所有数据的name值都映射成hash值,例如可以将“商品表”中的商品名“iphone8”映射成MD5计算出来的hash值b2dd48ff3e52d0796675693d08fb192e,然后再将全部name的hash值放入Redis中,从而构建出一个“数据库中所有可查数据的hash仓库”。之后,每次在查询MySQL之前都会先查询这个hash仓库,如果要查询数据的hash值存在于仓库中,再进入MySQL做真实的查询,如果不存在则直接返回。

需要注意的是,由于不同数据的hash值在概率上时可能相同的,因此可能会漏掉对个别数据的拦截,如图9中的“B”。

图9 不同数据的Hash值相同而造成的问题

2.缓存雪崩

除了缓存穿透以外,在使用缓存时还需要考虑缓存雪崩的情况。缓存雪崩是指由于某种原因造成Redis突然失效,从而造成MySQL瞬间压力骤增,进而严重影响MySQL性能甚至造成MySQL服务宕机。以下是造成缓存雪崩的两个常见原因:

(1)Redis重启;

(2)Redis中的大量缓存对象都设置了相同的过期时间。

为了避免缓存雪崩的发生,可参考使用以下解决方案:

(1)搭建Redis集群,保证高可用;

(2)避免大量缓存对象的key集中失效,尽力让过期时间分配均匀一些,例如,可以给各个缓存的过期时间乘一个随机数;

(3)通过队列、锁机制等控制并发访问MySQL的线程数。

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:【文末领取】


     【下面是我整理的2023年最全的软件测试工程师学习知识架构体系图+全套资料】


一、Python编程入门到精通


二、接口自动化项目实战  

三、Web自动化项目实战


四、App自动化项目实战 

五、一线大厂简历


六、测试开发DevOps体系 

七、常用自动化测试工具


八、JMeter性能测试 

九、总结(文末尾部小惊喜)

生命不息,奋斗不止。每一份努力都不会被辜负,只要坚持不懈,终究会有回报。珍惜时间,追求梦想。不忘初心,砥砺前行。你的未来,由你掌握!

生命短暂,时间宝贵,我们无法预知未来会发生什么,但我们可以掌握当下。珍惜每一天,努力奋斗,让自己变得更加强大和优秀。坚定信念,执着追求,成功终将属于你!

只有不断地挑战自己,才能不断地超越自己。坚持追求梦想,勇敢前行,你就会发现奋斗的过程是如此美好而值得。相信自己,你一定可以做到!    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/868204.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

蓝牙耳机运动耳机哪个好、蓝牙运动耳机推荐

尽管我对健身运动一直保持着热情,但有时候由于体力不支,难免会感到坚持困难。幸好,每次去健身房我都会戴上耳机,当动感的音乐流入耳朵,运动变得更加有趣,即使疲惫也能坚持到最后一秒。然而,要实…

PyTorch翻译官网教程-LANGUAGE MODELING WITH NN.TRANSFORMER AND TORCHTEXT

官网链接 Language Modeling with nn.Transformer and torchtext — PyTorch Tutorials 2.0.1cu117 documentation 使用 NN.TRANSFORMER 和 TORCHTEXT进行语言建模 这是一个关于训练模型使用nn.Transformer来预测序列中的下一个单词的教程。 PyTorch 1.2版本包含了一个基于论…

对于生产者消费者/shutdown/close的补充

信号量解决生产者消费者/读写者问题_右大臣的博客-CSDN博客 一点补充 模拟简单的string&#xff0c;循环队列&#xff0c;vector_右大臣的博客-CSDN博客 补充总结 写一个循环队列 用个循环队列去表示class myqueue{ vector<int>qq capacity 容量 front 头 rear 尾…

Python web实战之Django 的 WebSocket 支持详解

关键词&#xff1a;Python, Django, WebSocket, Web 如何使用 Django 实现 WebSocket 功能&#xff1f;本文将详细介绍 WebSocket 的概念、Django 的 WebSocket 支持以及如何利用它来创建动态、响应式的 Web 应用。 1. WebSocket 简介 1.1 什么是 WebSocket&#xff1f; 在 W…

阿里云Windows服务器怎么安装多个网站?

本文阿里云百科介绍如何在Windows Server 2012 R2 64位系统的ECS实例上使用IIS服务器搭建多个Web站点。本教程适用于熟悉Windows操作系统&#xff0c;希望合理利用资源、统一管理站点以提高运维效率的用户。比如&#xff0c;您可以在一台云服务器上配置多个不同分类的博客平台或…

前端跨域问题解决方法

跨域是WEB浏览器专有的同源限制访问策略。(后台接口调用和postman等工具会出现) 跨源资源共享&#xff08;CORS&#xff0c;或通俗地译为跨域资源共享&#xff09;是一种基于 HTTP 头的机制&#xff0c;该机制通过允许服务器标示除了它自己以外的其他源&#xff08;域、协议或端…

分类预测 | Matlab实现基于TSOA-CNN-GRU-Attention的数据分类预测

分类预测 | Matlab实现基于TSOA-CNN-GRU-Attention的数据分类预测 目录 分类预测 | Matlab实现基于TSOA-CNN-GRU-Attention的数据分类预测效果一览基本介绍研究内容程序设计参考资料 效果一览 基本介绍 Matlab实现分类预测 | Matlab实现基于TSOA-CNN-GRU-Attention的数据分类预…

Windows电脑快速搭建FTP服务教程

FTP介绍 FTP&#xff08;File Transfer Protocol&#xff09;是一种用于在计算机网络上进行文件传输的标准协议。它提供了一种可靠的、基于客户端-服务器模型的方式来将文件从一个主机传输到另一个主机。在本文中&#xff0c;我将详细介绍FTP的工作原理、数据传输模式以及常见…

FLatten Transformer 简化版Transformer

今天在找论文时&#xff0c;看到一篇比较新奇的论文&#xff0c;在这里跟大家分享一下&#xff0c;希望可以给一些人提供一些思路。虽然现在Transformer 比较火&#xff0c;在分割上面也应用的比较多&#xff0c;但是我一直不喜欢用&#xff0c;其中一个原因是结构太复杂了&…

【JavaWeb】MySQL基础操作

1 通用语法规则 SQL语句可以单行或者多行书写&#xff0c;以分号结尾SQL语句不区分大小写&#xff0c;关键字建议使用大写单行注释 --注释内容&#xff08;通用&#xff09; # 注释内容&#xff08;MySQL独有&#xff09;多行注释 /* 注释内容 */ 2 语句 数据库 -- 查…

OpenCV实例(八)车牌字符识别技术(一)模式识别

车牌字符识别技术&#xff08;一&#xff09;模式识别 1.模式识别流程2. 模式识别方式 影响并导致汽车牌照内字符出现缺损、污染、模糊等情况的常见因素有照相机的性能、采集车辆图像时光照的差异、汽车牌照的清洁度等。为了提高汽车牌照字符识别的准确率&#xff0c;本节将把英…

开发过程中遇到的问题以及解决方法

巩固基础&#xff0c;砥砺前行 。 只有不断重复&#xff0c;才能做到超越自己。 能坚持把简单的事情做到极致&#xff0c;也是不容易的。 开发过程中遇到的问题以及解决方法 简单易用的git命令 git命令&#xff1a; 查看有几个分支&#xff1a;git branch -a 切换分支&#…

深入浅出cgroup

一、什么是cgroup Cgroup是linux内核用来控制系统资源的机制&#xff0c;它将操作系统中的所有进程以组为单位划分&#xff0c;给这一组进程定义对某一类资源特定的访问权限。Cgroup用子系统&#xff08;subsystem&#xff09;来描述所能控制的系统资源&#xff0c;子系统具有…

四、Netty

目录 4.1 原生IO存在的问题4.2 Netty官网说明4.3 Netty的优点4.4 Netty的版本 4.1 原生IO存在的问题 4.2 Netty官网说明 https://netty.io/ 4.3 Netty的优点 4.4 Netty的版本 netty 下载地址&#xff1a;

android 如何分析应用的内存(十八)终章——使用Perfetto查看内存与调用栈之间的泄露

android 如何分析应用的内存&#xff08;十八&#xff09; 在前面两篇文章中&#xff0c;先是介绍了如何用AS查看Android的堆内存&#xff0c;然后介绍了使用MAT查看 Android的堆内存。AS能够满足基本的内存分析需求&#xff0c;但是无法进行多个堆的综合比较&#xff0c;因此…

OptaPlanner笔记1

1.1 什么是OptaPlanner 每个组织都面临规划问题&#xff1a;为产品或服务提供有限的受约束的资源&#xff08;员工、资产、时间和金钱&#xff09;。OptaPlanner用来优化这种规划&#xff0c;以实现用更少的资源来做更多的业务。 这被称为Constraint Satisfaction Programming…

使用maven打包时如何跳过test,有三种方式

方式一 针对spring项目&#xff1a; <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <skipTests>true</skipTests> </configuration> …

Vim学习(三)—— Git Repo Gerrit

Git、Gerrit、Repo三者的概念及使用 三者各自作用&#xff1a; git&#xff1a;版本管理库&#xff0c;在git库中没有中心服务器的概念&#xff0c;真正的分布式。 repo&#xff1a;repo就是多个git库的管理工具。如果是多个git库同时管理&#xff0c;可以使用repo。当然使用…

探讨uniapp的navigator 页面跳转问题

navigator 页面跳转。该组件类似HTML中的<a>组件&#xff0c;但只能跳转本地页面。目标页面必须在pages.json中注册。 "tabBar": {"color": "#7A7E83","selectedColor": "#3cc51f","borderStyle": "bl…

五、Netty高性能架构设计

目录 5.1 线程模型基本介绍5.2 传统阻塞I/O服务模型5.2.1 工作原理5.2.2 阻塞IO模型特点5.2.3 阻塞IO存在的问题 5.3 Reactor模式5.3.1 针对传统阻塞IO服务模型的2个缺点&#xff0c;解决方案5.3.2 IO复用 线程池&#xff0c;就是Reactor模式设计的基本思想 5.1 线程模型基本介…