设计高并发系统需要考虑多个方面,包括架构设计、数据库设计、缓存设计、负载均衡、容错与容灾等。以下是设计高并发系统时需要考虑的关键方面:
综上所述,设计高并发系统需要综合考虑架构设计、性能优化、安全性和可靠性等多个方面,并不断进行监控和调优,以确保系统能够稳定、高效地运行。
并发
同时拥有两个或多个线程,如果程序在单核处理器上运行,多个线程将交替的换入或者换出内存,这些线程是同时“存在”的,每个线程都处于执行过程中的某个状态,如果运行在多核处理器上,此时,程序中的每个线程都将分配到一个处理器核上,因此可以同时运行。
高并发
高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求。
并发:多个线程操作相同的资源,保证线程安全,合理使用资源
高并发:服务能同时处理很多请求,提高程序性能(更多的考虑技术手段)
1.分层:分层是处理任何复杂系统最常见的手段之一,将系统横向切分成若干个层面,每个层面只承担单一的职责,然后通过下层为上层提供的基础设施和服务以及上层对下层的调用来形成一个完整的复杂的系统。计算机网络的开放系统互联参考模型(OSI/RM)和Internet的TCP/IP模型都是分层结构,大型网站的软件系统也可以使用分层的理念将其分为持久层(提供数据存储和访问服务)、业务层(处理业务逻辑,系统中最核心的部分)和表示层(系统交互、视图展示)。需要指出的是:
(1)分层是逻辑上的划分,在物理上可以位于同一设备上也可以在不同的设备上部署不同的功能模块,这样可以使用更多的计算资源来应对用户的并发访问;
(2)层与层之间应当有清晰的边界,这样分层才有意义,才更利于软件的开发和维护。
2.分割:分割是对软件的纵向切分。我们可以将大型网站的不同功能和服务分割开,形成高内聚低耦合的功能模块(单元)。在设计初期可以做一个粗粒度的分割,将网站分割为若干个功能模块,后期还可以进一步对每个模块进行细粒度的分割,这样一方面有助于软件的开发和维护,另一方面有助于分布式的部署,提供网站的并发处理能力和功能的扩展。
系统集群化部署
数据库层面的分库分表+读写分离
针对读多写少的请求,引入缓存集群
针对高写入的压力,引入消息中间件集群
页面静态化、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 线程内存
我们项目的架构采用云原生架构
在 Java 高并发环境下,设计 A 服务调用 B 服务需要考虑以下几个方面,在Java中,设计一个高并发的服务调用另一个服务需要考虑以下几个关键点:
在Java中,设计一个高并发的服务调用另一个服务,可以使用以下几种方法:
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的代码
});
}
}
import java.util.concurrent.*;
public class ServiceCaller {
private static final ExecutorService executorService = Executors.newCachedThreadPool();
public static void callServiceAAsync(CompletableFuture callback) {
CompletableFuture.runAsync(() -> {
// 调用服务A的代码
callback.complete(null);
}, executorService).exceptionally(e -> {
callback.completeExceptionally(e);
return null;
});
}
public static void callServiceBAsync(CompletableFuture callback) {
CompletableFuture.runAsync(() -> {
// 调用服务B的代码
callback.complete(null);
}, executorService).exceptionally(e -> {
callback.completeExceptionally(e);
return null;
});
}
}
预热(Warm-up)是计算机编程中的一个概念,通常用于提高应用程序的性能。预热过程可以包括对代码、缓存、数据库和其他资源进行预热,使其达到最佳性能状态。
在 Java 中,预热通常指的是在应用程序启动后,对某些资源进行初始化或加载,以便在后续的运行中能够更快地响应用户请求。
预热的作用主要有以下几点: