无状态
系统的处理方式不依赖于之前的请求状态或上下文信息。每个请求都是独立的,服务器不会保存客户端的状态信息。实际生产环境:引用无状态,配置文件有状态不同的机房 需要不同的数据源,需要通过配置文件或配置中心指定。
无状态得设计有以下特点:
-
无状态服务器:服务器不会在自身中保存客户端的状态信息。每个请求都包含了足够的信息,服务器可以独立处理请求并返回结果。这样的设计可以提高系统的可伸缩性和容错性。
-
横向扩展:由于服务器不需要保存客户端的状态信息,可以通过添加更多的服务器实例来扩展系统的处理能力。每个实例都可以独立地处理请求,而无需共享状态,从而实现更好的并发性能。
-
负载均衡:由于每个请求都是独立的,可以使用负载均衡器将请求分发到不同的服务器实例上,以平衡负载并提高系统的性能和可用性。
-
可靠性和可恢复性:由于服务器不依赖于之前的请求状态,即使某个服务器实例发生故障,系统仍然可以继续处理其他请求。故障的服务器可以被替换或重新启动,而无需影响整个系统的运行。
拆分
系统的功能、数据或负载分割成多个部分,以便能够更好地处理并发请求和提高系统的性能 。如果用户量/交易量不大,开发人少,没必要拆分系统。对于我们熟悉的秒杀系统,访问量非常大,资源充足,考虑拆分系统。
1.系统维度: 按照系统功能/业务拆分,比如商品系统、购物车、结算、订单系统。
2.功能维度: 对一个系统进行功能再拆分,比如,优惠卷系统可以拆为后台卷创建系统、领卷系统、用卷系统
3.读写维度: 根据读写比例特征进行拆分。 商品系统,交易的各个系统拆分成商品写服务,商品读服务:都可以考虑使用缓存提升性能;写量大考虑分库分表;聚合读取的场景如商品详情页,考虑数据异构拆分系统,将分散的数据聚合在一处存储,一提升系统的性能和可靠性。
4.AOP维度: 根据访问特征,按照AOP进行拆分,比如,商品详细页分为CDN、页面渲染系统。
5.模板维度: 按照基础或者代码维护特征进行拆分、基础模块分库分表、数据库连接池等; (Web、service、DAO)。
服务化
系统的功能和业务逻辑划分为独立的服务,每个服务都可以独立开发、部署和扩展。服务化是一种基于服务架构的设计思想,用于构建可扩展、松耦合和易于维护的高并发系统。
服务化通过包含以下特点。
1.将系统拆分成多个小型的服务。每个服务负责特定的功能,可以独立开发、部署和扩展。
-
服务化系统通过网络进行服务间的通信(消息队列、RPC等)。
-
服务化系统通常需要一些服务治理机制来管理和监控服务的运行状态。
-
服务化系统通常借助自动化工具和技术,实现自动化的部署、扩展和运维(k8s、docker)。
消息队列
消息队列是一种在高并发系统中用于实现服务解耦、异步操作、流量销峰/缓冲的通信机制。它允许应用程序通过发送和接收消息来进行通信,而不需要直接相互调用或知道对方的存在。 比如,电商系统中的交易订单数据,该数据有非常多的系统关心并订阅,比如,订单生产系统、定期送系统、订单风控系统等等。如果订阅者太多,那么订阅单个消息队列就会成为瓶颈,此时需要考虑对消息队列进行镜像复制。
使用消息队列对于不能容忍失败的业务场景,做持久化数据要同时增加日志、报警。对于消息重复问题,特别是分布式消息队列,处于对性能和开销的考虑,在一些场景下回发生消息重复接受,需要在业务层面进行防重处理。
数据异构
数据异构是指在系统中使用不同的数据存储和处理技术,以适应不同的需求和场景。
比如,订单分库分表一般按照订单ID进行分,查询某个用户的订单列表,需要聚合多个表数据才能返回,此时需要异构一套用户订单表,按照用户ID进行分库分表。还需要考虑对历史订单数据进行归档处理,提升服务的性能和稳定性。
在高并发系统设计中,数据异构可以带来以下好处:
-
数据存储选择:根据数据的特点和使用场景,选择合适的数据存储技术。例如,关系型数据库适合处理结构化数据,NoSQL数据库适合处理大规模非结构化数据,内存数据库适合处理需要快速读写的数据等。
-
数据分区和拆分:将数据分区存储在多个节点或数据库中,以提高系统的并发处理能力和扩展性。可以根据数据的特点和访问模式进行拆分,例如按照用户ID、地理位置、时间范围等进行拆分。
-
缓存层:引入缓存层来加速数据的读取和降低后端存储的负载压力。常见的缓存技术包括内存缓存(如Redis)、分布式缓存(如Memcached)等。缓存层可以提高系统的响应速度和并发处理能力。
-
异步处理和消息队列:将数据的处理过程异步化,通过消息队列将数据传递给后续处理步骤。这样可以减少请求的响应时间,并提高系统的并发处理能力。
-
数据复制和冗余:通过数据复制和冗余机制,提高系统的可用性和容错性。可以将数据复制到多个节点或数据中心,并通过数据同步和故障切换来保证数据的可靠性和可恢复性。
数据闭环
数据闭环如商品详情页,因为数据来源太多,影响服务稳定性的因素就非常多了因此,最好的办法是把使用到的数据进行异构存储,形成数据闭环。
缓存
1.浏览器端缓存
浏览器端缓存是指浏览器在本地存储和管理已经请求过的资源的副本,以便在后续的请求中直接使用,而无需再次从服务器获取相同的资源。 通过设置响应头中的Cache-Control
和Expires
字段,服务器可以告知浏览器在一段时间内可以直接使用缓存的资源,而无需重新请求 。对于实时性不敏感的数据适用。
2.APP客户端缓存
为了防止流量冲击,可以将一些需要访问的素材(js/image等)提前发到客户端进行缓存 可以提高应用程序的响应速度、减少对服务器的请求次数以及在离线状态下继续使用的能力。 但需要注意的是,缓存的使用需要合理考虑缓存的有效期、更新策略和缓存容量的管理,以避免占用过多的设备存储空间或使用过期的数据。
3.CDN缓存
有些页面、活动页、图片等服务可以考虑将页面、活动页、图片推送到离用户最近的 CDN 节点,让用户能在离他最近的节点找到想要的数据。
-
内容分发网络(Content Delivery Network):CDN是由多个分布在全球各地的服务器节点组成的网络。这些节点被部署在离用户较近的位置,以提供更快的内容传输和更低的延迟。
-
缓存服务器:CDN网络中的每个节点都包含一个或多个缓存服务器,用于存储和提供静态资源的副本。这些静态资源可以是图片、CSS文件、JavaScript文件、视频等。
-
缓存规则:CDN缓存规则是指定义在CDN节点上的一组规则,用于决定哪些资源应该被缓存、缓存的时间以及如何处理缓存的更新和失效。缓存规则可以根据资源的URL、文件类型、缓存策略等进行配置。
-
缓存机制:CDN缓存机制是指根据缓存规则和用户请求的资源来决定是否从缓存服务器中获取资源。当用户请求一个资源时,CDN会先检查是否存在缓存副本,如果存在并且满足缓存规则,则直接返回缓存的资源;否则,CDN节点会从源服务器获取最新的资源,并在适当的情况下更新缓存。
CDN缓存可以提高用户的访问速度、降低服务器负载,并提供更好的可靠性和可扩展性。通过将静态资源缓存在离用户更近的CDN节点上,可以减少网络延迟和带宽消耗,从而提供更好的用户体验。
4.接入层缓存
接入层缓存是指在系统架构中位于应用程序和后端服务之间的一层缓存。它位于整个系统的接入层,用于缓存处理过的请求和响应数据,以提高系统的性能和扩展性。
可考虑如下机制实现
URL重写:将URL按照指定的顺序或者格式重写,去除随机数
一致性哈希:按照指定的参数(如分类/商品编号)做一致性Hash,从而保证相同数据落到一台服务器上。proxy_cache:使用内存级/SSD级代理缓存来缓存内容
proxy_cache lock:使用lock机制,将多个回源合并为一个,以减少回源量并设置相应的lock超时时间。 shared dict:如果架构使用了nginx+lua实现,则可以考虑使用lua shared dict进行cache,最大的好处就是reload缓存不会丢失。
接入层缓存可以有效地减少对后端服务和数据库的访问次数,提高系统的响应速度和并发处理能力。它可以应用于各种类型的系统架构,包括微服务架构、分布式系统和大规模Web应用等。但需要注意的是,缓存的使用需要权衡数据的实时性、一致性和缓存的更新策略,以避免数据不一致和过期的问题。
5.应用层缓存
堆内缓存和堆外缓存
-
堆内缓存:堆内缓存是指将数据存储在应用程序的内存中。这种缓存方式使用应用程序的堆内存作为缓存存储空间,可以通过使用变量、集合或自定义数据结构来实现。堆内缓存具有快速的读写速度和低延迟,适用于小规模数据集和快速读取的场景。
-
堆外缓存:堆外缓存是指将数据存储在应用程序之外的缓存介质中,如磁盘、数据库、分布式缓存等。常见的堆外缓存包括Redis、Memcached等。堆外缓存可以处理大规模数据集和持久化需求,并提供更高的缓存容量和可扩展性。
6.分布式缓存
分布式缓存是指将缓存数据分散存储在多台机器上,以提供更大的缓存容量、高可用性和可扩展性的缓存解决方案。它通过将数据分布在多个节点上,使得缓存系统可以处理大规模的数据集和高并发的访问请求。
有一种机制是要废弃分布式缓存,改成应用 localredis cache 情况下如果数据量不大这种架构是最优的。但是如果数据量太大,单服务器存储不了,那么可以使用分片机制将流量分散到多台,或者直接用分布式缓存实现。常见的分片规则就是一致性哈希了。
池化技术
池化技术是一种资源管理技术,通过事先创建一组可重用的资源池,以提高资源的利用率和性能。在池化技术中,资源(如数据库连接、线程、网络连接等)被预先创建并存储在一个池中,当需要使用资源时,从池中获取资源并进行操作,完成后将资源返回到池中,以供后续的请求使用。
常见的池化技术包括以下几种:
-
数据库连接池:用于管理数据库连接的池化技术,通过事先创建一组数据库连接并存储在连接池中,应用程序在需要访问数据库时,从连接池中获取连接并执行数据库操作,完成后将连接返回到连接池中。常见的数据库连接池有Apache Commons DBCP、HikariCP等。
-
线程池:用于管理线程的池化技术,通过预先创建一组线程并存储在线程池中,应用程序在需要执行任务时,从线程池中获取线程执行任务,任务完成后线程返回到线程池中,以便重复使用。线程池可以提高线程的创建和销毁效率,同时也可以限制并发线程数量,避免资源过度占用。Java中的java.util.concurrent包提供了线程池的实现。
-
连接池:用于管理网络连接的池化技术,比如连接池可以用于管理HTTP连接、FTP连接等。连接池通过创建一组可重用的连接对象,并在需要时从连接池中获取连接,完成后将连接返回到连接池中。这可以提高网络连接的利用率和性能。
-
对象池:用于管理任意类型对象的池化技术,通过创建一组可重用的对象并存储在对象池中,应用程序在需要使用对象时,从对象池中获取对象并进行操作,完成后将对象返回到对象池中。对象池可以用于管理各种资源,如数据库连接、线程、网络连接等。
并发
并发化是指在软件系统中同时执行多个独立的任务或操作的能力。通过并发化,可以让多个任务在同一时间段内并行执行,从而提高系统的性能、响应速度和资源利用率。
在并发化中,任务可以是线程、进程或其他独立执行的操作单元。通过将任务分配给不同的执行单元,并发执行可以充分利用多核处理器或分布式系统的资源,实现任务的并行处理。
比如应用中一个服务会调用多个依赖服务来处理业务,而这些依赖服务是可以同时调用的。如果顺序调用的话需要耗时 100ms,而并发调用只需要 50ms,那么可以使用 Java 并发机制来并发调用依赖服务,从而降低该服务的响应时间。