高并发系统设计

概述

设计高并发系统需要考虑多个方面,包括架构设计、数据库设计、缓存设计、负载均衡、容错与容灾等。以下是设计高并发系统时需要考虑的关键方面:

  1. 水平扩展(Horizontal Scaling): 高并发系统通常需要水平扩展以应对大量的并发请求。这可以通过在系统的各个层级(如应用服务器、数据库等)增加更多的节点或实例来实现。
  2. 负载均衡(Load Balancing): 使用负载均衡器来分发流量到多个服务器或实例上,确保每个服务器都能够处理适当的负载。负载均衡器可以基于不同的策略(如轮询、最少连接等)进行流量分发。
  3. 缓存优化(Caching): 使用缓存来减轻数据库和其他后端服务的压力。常见的缓存技术包括内存缓存(如Redis、Memcached)和分布式缓存(如Redis Cluster、Memcached Cluster)等。
  4. 数据库优化(Database Optimization): 在高并发系统中,数据库往往是性能瓶颈之一。可以通过采用合适的数据库引擎、优化查询语句、建立合适的索引、分库分表等方式来优化数据库性能。
  5. 异步处理(Asynchronous Processing): 将一些耗时的操作(如文件上传、邮件发送等)改为异步处理,以减少请求的响应时间,并提高系统的吞吐量。
  6. 容错与容灾(Fault Tolerance and Disaster Recovery): 设计系统时需要考虑容错和容灾机制,包括备份、数据同步、故障转移、自动恢复等,以确保系统在面对故障或灾难时能够继续正常运行。
  7. 监控与调优(Monitoring and Tuning): 部署监控系统来实时监测系统的性能指标、服务可用性等,并根据监控数据进行系统调优,及时发现和解决潜在的性能问题。
  8. 安全性(Security): 在设计高并发系统时要考虑安全性,包括数据加密、访问控制、防火墙、DDoS攻击防护等,以保护系统免受恶意攻击。
  9. 限流与熔断(Rate Limiting and Circuit Breaking): 实施限流机制来控制系统的请求流量,避免过载。同时,引入熔断机制可以在系统出现故障或不稳定时快速失败,保护系统的稳定性。
  10. 持续优化(Continuous Optimization): 高并发系统的设计是一个持续优化的过程,需要不断地根据实际情况进行调整和改进,以适应不断变化的业务需求和用户规模。

综上所述,设计高并发系统需要综合考虑架构设计、性能优化、安全性和可靠性等多个方面,并不断进行监控和调优,以确保系统能够稳定、高效地运行。

并发
同时拥有两个或多个线程,如果程序在单核处理器上运行,多个线程将交替的换入或者换出内存,这些线程是同时“存在”的,每个线程都处于执行过程中的某个状态,如果运行在多核处理器上,此时,程序中的每个线程都将分配到一个处理器核上,因此可以同时运行。
高并发
高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求。
并发:多个线程操作相同的资源,保证线程安全,合理使用资源
高并发:服务能同时处理很多请求,提高程序性能(更多的考虑技术手段)

大型网站在架构上应当考虑哪些问题

1.分层:分层是处理任何复杂系统最常见的手段之一,将系统横向切分成若干个层面,每个层面只承担单一的职责,然后通过下层为上层提供的基础设施和服务以及上层对下层的调用来形成一个完整的复杂的系统。计算机网络的开放系统互联参考模型(OSI/RM)和Internet的TCP/IP模型都是分层结构,大型网站的软件系统也可以使用分层的理念将其分为持久层(提供数据存储和访问服务)、业务层(处理业务逻辑,系统中最核心的部分)和表示层(系统交互、视图展示)。需要指出的是:
(1)分层是逻辑上的划分,在物理上可以位于同一设备上也可以在不同的设备上部署不同的功能模块,这样可以使用更多的计算资源来应对用户的并发访问;
(2)层与层之间应当有清晰的边界,这样分层才有意义,才更利于软件的开发和维护。

2.分割:分割是对软件的纵向切分。我们可以将大型网站的不同功能和服务分割开,形成高内聚低耦合的功能模块(单元)。在设计初期可以做一个粗粒度的分割,将网站分割为若干个功能模块,后期还可以进一步对每个模块进行细粒度的分割,这样一方面有助于软件的开发和维护,另一方面有助于分布式的部署,提供网站的并发处理能力和功能的扩展。

  • 分布式:除了上面提到的内容,网站的静态资源(JavaScript、CSS、图片等)也可以采用独立分布式部署并采用独立的域名,这样可以减轻应用服务器的负载压力,也使得浏览器对资源的加载更快。数据的存取也应该是分布式的,传统的商业级关系型数据库产品基本上都支持分布式部署,而新生的NoSQL产品几乎都是分布式的。当然,网站后台的业务处理也要使用分布式技术,例如查询索引的构建、数据分析等,这些业务计算规模庞大,可以使用Hadoop以及MapReduce分布式计算框架来处理。
  1. 集群:集群使得有更多的服务器提供相同的服务,可以更好的提供对并发的支持。
    4.缓存:所谓缓存就是用空间换取时间的技术,将数据尽可能放在距离计算最近的位置。使用缓存是网站优化的第一定律。我们通常说的CDN、反向代理、热点数据都是对缓存技术的使用。
    5.异步:异步是实现软件实体之间解耦合的又一重要手段。异步架构是典型的生产者消费者模式,二者之间没有直接的调用关系,只要保持数据结构不变,彼此功能实现可以随意变化而不互相影响,这对网站的扩展非常有利。使用异步处理还可以提高系统可用性,加快网站的响应速度(用Ajax加载数据就是一种异步技术),同时还可以起到削峰作用(应对瞬时高并发)能推迟处理的都要推迟处理"是网站优化的第二定律,而异步是践行网站优化第二定律的重要手段。
    6.冗余:各种服务器都要提供相应的冗余服务器以便在某台或某些服务器宕机时还能保证网站可以正常工作,同时也提供了灾难恢复的可能性。冗余是网站高可用性的重要保证。
    在大部分场景下,没有必要太过于关注线程池大小怎么配置,I/O密集型任务使用Netty默认配置就可以了。
    因为,提高吞吐量也不能只简单的只依赖线程池,还可以通过缓存、微服务拆分,优化业务逻辑、优化算法等
    方式来协作解决。

高并发解决方案

系统集群化部署
数据库层面的分库分表+读写分离
针对读多写少的请求,引入缓存集群
针对高写入的压力,引入消息中间件集群
页面静态化、cdn加速、缓存、异步、多线程处理
分库分表、池化技术、读写分离
索引、批处理、集群、负载均衡、限流、服务降级、故障转移
异地多活、压测、监控
高并发解决思路与手段
扩容:水平扩容、垂直扩容
缓存:Redis、Memcache、GuavaCache等
队列:Kafka、RabitMQ、RocketMQ等
应用拆分:服务化Dubbo与微服务Spring Cloud
限流:Guava RateLimiter使用、常用限流算法、自己实现分布式限流等
服务降级与服务熔断:服务降级的多重选择、Hystrix
数据库切库,分库分表:切库、分表、多数据源
高可用的一些手段:任务调度分布式elastic-job、主备curator的实现、监控报警机制
rocketmq发消息异步处理业务
xxljob做补偿
cannal、Kafka、数据同步
使用 openresty/nginx 对服务器实现集群 保证服务器的高可用
Jvm、mysql、tomcat、redis 优化

系统在设计之初就会有一个预估容量,长时间超过系统能承受的TPS/QPS阈值,系统可能会被压垮,最终导致整个服务不够用。为了避免这种情况,我们就需要对接口请求进行限流。
限流的目的是通过对并发访问请求进行限速或者一个时间窗口内的的请求数量进行限速来保护系统,一旦达到限制速率则可以拒绝服务、排队或等待。
一般开发高并发系统常见的限流模式有控制并发和控制速率,一个是限制并发的总数量(比如数据库连接池、线程池),一个是限制并发访问的速率(如nginx的limit_conn模块,用来限制瞬时并发连接数),另外还可以限制单位时间窗口内的请求数量(如Guava的RateLimiter、nginx的limit_req模块,限制每秒的平均速率)。其他还有如限制远程接口调用速率、限制MQ的消费速率。另外还可以根据网络连接数、网络流量、CPU或内存负载等来限流。

相关概念:
PV:page view 页面总访问量,每刷新一次记录一次。
UV:unique view 客户端主机访问,指一天内相同IP的访问记为1次。
QPS:query per second,即每秒访问量。qps很大程度上代表了系统的繁忙度,没次请求可能存在多次的磁盘io,网络请求,多个cpu时间片,一旦qps超过了预先设置的阀值,可以考量扩容增加服务器,避免访问量过大导致的宕机。
RT:response time,每次请求的响应时间,直接决定用户体验性。

从多个维度谈前端→网关→后端→运维→监控→硬件
前端
动静分离区别 将静态资源和动态资源完全分开部署,需要将静态资源存入到第三方 cdn 产商中 例如阿里云 oss、七牛云 等 价格大概在每 GB/0.5 元左右,CDN 会在全国各地都有节点,遵循用户就近原则访问,从而提高用户访问体验。
需要对引入的静态资源做压缩 例如 生成.min 文件,减少占用宽带资源
对静态资源做二次压缩,CDN 缺陷:刷新 cdn 缓存缺陷
在外网中传输我们的数据 有带宽限制。基本上我们的网站 一个网页 静态资源是占用带宽 80%
前后端分离区别 前端开发 和 后端分开开发模式 开发模式
动静分离区别 将静态资源和动态资源完全分开部署后端

JVM、数据库、缓存、MQ、网关层面、硬件层面等
JVM 层面 需要对 JVM 实现参数调优 减少 gc 回收频率 减少 stw 问题
根据服务器配置和业务需求,选择合适的GC 垃圾收集器 例如 ZGC、G1 等
使用 redis 缓存减少对数据库的访问压力(mysql 与 redis 数据一致性问题、雪崩、穿透、击穿)
数据库层面 定位慢查询 sql 语句优化 索引优化 索引遵循最左匹配原则 分表分库 使用范围、一致性 hash 分片
利用 MQ 实现流量削峰问题,MQ 消费者根据自身合适能力来进行消费,为了避免消息堆积建议 MQ 消费者集群和批量消费消息,整合k8s 当流量突然大的时候快速扩容与缩容。
利用网关层面对服务器接口实现保护,通过限流配置预防突发大流量对系统的冲击。限流规则:频率 底层算法采用 令牌桶、漏桶、滑动窗口算法
限流框架:谷歌 guava、阿里巴巴 sentinel、基于 redis 等
整合 java 中 getaway 或者 nginx+lua --手写限流算法
当系统出现过多的异常或慢请求,上游服务则需开启熔断 当下游的服务因为某种原因导致服务不可用或响应过慢时,上游服务为了保证自己整体服务的可用性,不再继续调用目标服务,直接返回。当下游服务恢复后,上游服务会恢复调用 底层采用滑动窗口算法实现
服务降级、限流、熔断 隔离机制

运维层面
通过整合 SkyWalking 分布式追踪系统 实现服务监控 当服务器发生了 cpu 使用率飙高、内存溢出 等问题 能够提前预防告警,底层是基于 agent代理实现。
利用分布式追踪系统排查 RPC 远程调用过程中某链发生错误问题,底层采用spanid、全局 id 记录。
构建分布式主动告警系统,当系统发生错误采用公众号消息模板主动将错误推送给开发者,开发者无需登录服务器端查看错误日志。
构建分布式日志采集系统 elk+kafka,采集分布式系统的日志访问层面:
高并发的情况下需要接入第三方 DDOS 防御系统 不能暴露用户真实的 IP 地址。
通过 ddos 系统可以配置防御 (图形验证码)防止机器模拟请求攻击等
硬件层面:硬盘选择(固态硬盘)
Cpu 核心数 物理机器服务器 64 核 128 线程内存
我们项目的架构采用云原生架构

高并发如何设计a服务调用b服务

在 Java 高并发环境下,设计 A 服务调用 B 服务需要考虑以下几个方面,在Java中,设计一个高并发的服务调用另一个服务需要考虑以下几个关键点:

  1. 异步调用:为了提高 A 调用 B 的效率,可以使用异步方式调用 B 服务。具体可以使用 Java 并发包中的 CompletableFuture 或者 Netty 的 ChannelFuture 等方式来实现异步调用。
  2. 线程池:为了避免 A 调用 B 服务时阻塞 A 服务的主线程,可以使用线程池来处理 B 服务的响应。通过使用线程池,可以让 A 服务的主线程专注于处理其他请求,从而提高系统的并发性能。
  3. 限流与熔断:为了避免 B 服务出现故障或者压力过大导致 A 服务响应变慢,可以引入限流和熔断机制。具体可以使用 Hystrix 等开源框架来实现限流和熔断功能。在高并发的系统中,如果没有适当的限流和熔断机制,可能会导致系统崩溃。因此,你需要使用一些限流和熔断技术来保护系统。例如,你可以使用Hystrix或者Resilience4j这样的库来实现熔断和限流。
  4. 缓存:如果 B 服务需要频繁地被调用,可以使用缓存来减少对 B 服务的直接调用次数,提高系统性能。可以使用 Redis 等内存数据库来实现缓存功能。
  5. 异常处理:在 A 调用 B 服务时,可能会出现各种异常情况,例如网络不稳定、超时等。因此,在设计中需要考虑异常处理机制,例如使用 try-catch 块来捕获异常,并根据具体情况进行处理。错误处理:当服务调用失败时,需要有适当的错误处理机制。这可能涉及到记录错误信息,通知系统管理员, 或者在合适的时候重试请求。
  6. 服务端设计:你需要设计一个能够处理高并发的服务端。这通常涉及到使用一些支持高并发的技术,例如Spring Boot,或者使用无服务器架构,例如AWS Lambda。
  7. 客户端设计:客户端需要能够并发地发送请求到服务端。这通常涉及到使用线程池或者使用异步调用来提高并发性能。
  8. 负载均衡:你可能需要一个负载均衡器来分发客户端的请求到多个服务实例,以便能够更好地处理并发负载。这可以使用一些开源的负载均衡器,例如Nginx,或者使用云服务提供商的负载均衡服务,例如AWS Elastic Load Balancer。
  9. 超时和重试:在设计服务调用时,你需要考虑到网络延迟和服务处理时间可能导致的问题。例如,你可能需要设置超时时间,并且在超时后重试请求。
  10. 日志和监控:为了确保系统的健康和高可用性,你需要有适当的日志和监控系统。这可以帮助你及时发现和解决问题。
    在设计高并发的服务调用时,需要考虑到很多因素,并且需要进行适当的测试和调优来确保系统的性能和可用性。综上所述,在 Java 高并发环境下设计 A 服务调用 B 服务需要考虑多个方面,包括异步调用、线程池、限流与熔断、缓存和异常处理等。通过合理的设计和实现,可以提高系统的性能和稳定性。

在Java中,设计一个高并发的服务调用另一个服务,可以使用以下几种方法:

  1. 使用线程池(ThreadPoolExecutor):创建一个固定大小的线程池,用于执行服务调用。这样可以有效地控制并发线程的数量,避免过多的线程导致系统资源耗尽。
import java.util.concurrent.*;

public class ServiceCaller {
    private static final int THREAD_POOL_SIZE = 10;
    private static final ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);

    public static void callServiceA() {
        executorService.submit(() -> {
            // 调用服务A的代码
        });
    }

    public static void callServiceB() {
        executorService.submit(() -> {
            // 调用服务B的代码
        });
    }
}
  1. 使用异步回调(CompletableFuture):通过异步回调的方式,可以在不阻塞当前线程的情况下执行服务调用。当服务调用完成后,可以通过回调函数处理结果。
import java.util.concurrent.*;

public class ServiceCaller {
    private static final ExecutorService executorService = Executors.newCachedThreadPool();

    public static void callServiceAAsync(CompletableFuture<Void> callback) {
        CompletableFuture.runAsync(() -> {
            // 调用服务A的代码
            callback.complete(null);
        }, executorService).exceptionally(e -> {
            callback.completeExceptionally(e);
            return null;
        });
    }

    public static void callServiceBAsync(CompletableFuture<Void> callback) {
        CompletableFuture.runAsync(() -> {
            // 调用服务B的代码
            callback.complete(null);
        }, executorService).exceptionally(e -> {
            callback.completeExceptionally(e);
            return null;
        });
    }
}
  1. 使用消息队列(如RabbitMQ、Kafka等):将服务A和服务B之间的调用关系定义为消息,通过消息队列进行异步通信。这样可以避免直接调用对方服务,降低耦合度,提高系统的可扩展性。
  2. 使用远程过程调用(RPC):如果服务A和服务B之间存在复杂的交互逻辑,可以考虑使用远程过程调用技术,如gRPC、Dubbo等。这些技术可以将服务A和服务B封装成独立的服务,并通过接口进行通信。

预热

预热(Warm-up)是计算机编程中的一个概念,通常用于提高应用程序的性能。预热过程可以包括对代码、缓存、数据库和其他资源进行预热,使其达到最佳性能状态。
在 Java 中,预热通常指的是在应用程序启动后,对某些资源进行初始化或加载,以便在后续的运行中能够更快地响应用户请求。
预热的作用主要有以下几点:

  1. 优化性能:通过预热,可以使得代码、缓存、数据库等资源达到最佳性能状态,从而提高应用程序的运行效率。
  2. 减少延迟:在应用程序启动时,可能需要进行一些初始化和配置操作,这些操作可能需要一定的时间来完成。通过预热,可以提前完成这些操作,从而减少应用程序启动时的延迟。加速应用程序启动:通过预先加载资源或执行一些初始化操作,可以在应用程序启动后更快地准备好处理请求,提高用户体验。
  3. 提高稳定性:预热可以使得应用程序更加稳定,因为预热可以使得资源达到最佳状态,从而减少运行时可能出现的问题。一些 JIT 编译器需要经过一定的时间才能对代码进行优化编译,预热可以让 JIT 编译器有更多的机会对代码进行优化,从而提高应用程序的性能和稳定性。
  4. 避免“冷启动”问题:在某些场景下,如果应用程序长时间没有使用,可能会因为缓存失效等原因导致性能下降,预热可以在应用程序重新活跃起来时避免这种“冷启动”问题。
    在 Java 中,可以通过在应用程序启动时手动触发一些初始化操作或者利用一些框架提供的预热机制来实现预热。例如,在 Spring 框架中,可以使用 @PostConstruct 注解来标记某些方法,在应用程序启动时自动执行初始化操作,达到预热的效果。
    在Java中,预热可以通过多种方式实现,例如:
  5. 启动时加载部分数据:在应用程序启动时,可以加载部分数据到缓存中,以便后续的请求能够更快地获取到数据。
  6. 运行时动态调整:在应用程序运行时,可以根据实际情况动态调整资源的使用,例如调整线程池的大小、调整缓存的策略等。
  7. 使用预热工具:可以使用一些预热工具来帮助实现预热,例如使用Java的Profile工具来监控应用程序的性能,并根据性能数据来调整资源的使用。
    总之,预热是提高应用程序性能和稳定性的重要手段之一。在Java中,可以通过多种方式实现预热,具体实现方式需要根据实际情况来选择。
  • 32
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

思静语

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值