性能优化 场景题
调用第三方接口应注意哪些问题?
主要需关注以下几点:
- 超时控制:必须设置连接超时(如3秒)和读取超时(如5秒),避免被拖死
- 重试策略:外部接口可能不太稳定,对于非幂等接口(如支付)禁止重试,其他有限重试(最多3次)+退避策略(如间隔1秒、3秒)等。
- 熔断降级:外部接口不可控,为了保护我们的系统,必要时候可以用 Hystrix/Sentinel 在故障时快速失败,避免服务雪崩。
- 数据安全:有时候参数比较敏感,比如手机号、身份证等等,这种一定要求参数加密(如手机号AES加密),HTTPS强制校验证书,避免数据被抓包泄露。
除此之外,还要进行监控埋点,比如记录调用量、耗时、错误码,配置异常报警(如10分钟超时5次)等等
超时设置的隐藏坑
很多HTTP客户端默认不超时(如Java的HttpURLConnection),所以一定要手动设置,这个是最保险的
然后针对不同业务设置不同的超时时间,比如支付类接口,设置短超时(2秒)快速失败。
如果是报表导出类,超时可适当放宽(30秒),但必须异步化。
千万不要为了减少超时报措,把超时时间都设置很长,因为我们系统的线程资源是有限的,如果超时时间设置的很长,对方服务一旦出了问题,很多线程就会被 hold 住等待超时,那么线程池被占满后,服务就会拒绝服务,然后瘫痪。
所以宁愿快速失败!
重试机制
尽量要求三方接口提供幂等性支持,比如要求第三方接口提供唯一流水号(如 request_id),这样我们系统处理起来就比较简单。
然后采用退避策略来优化重试,把立即重试改成随机延迟重试(避免惊群效应)或者第一次失败等1秒,第二次等3秒(线性补偿)等。
一定要确认接口的幂等性,不然重试会出问题,比如盲目重试短信接口,用户收到十几条重复短信。
公司目前有一些海外业务,从国内将数据发送到海外延迟比较大,如何处理这个问题?
国内到海外的数据传输:延时大实际是因为物理因素(距离远)导致的,无法避免
因此优化的根本思路就是避免或减少实时跨国数据传输
将服务部署本地化,在海外对应的业务区域部署完整的应用服务和数据库副本,用户访问和服务交互直接在海外本地完成,这个操作会大幅峰低请求延迟。(很多国家实际也要求必须本地化服务)
部分数据必须要同步的可以预同步。建立后台异步同步机制,比如利用数据库主从复制、消息队列等。利用专线提前将数据推送至海外数据中心。(在前期)可能还会有一些无法避免的跨国实时请求,这部分请求只能网络层面优化,使用 CDN、专线或 SD-WAN 优化传输路径。对这些要传输的数据进行压缩,如 Gzip、Snappy等,减少传输体积,提升带宽利用率。
接口变慢了应该如何排查?导致接口变慢的原因有哪些?
接口变慢的原因可能非常复杂,涉及到 硬件资源、网络、应用程序代码、数据库 等各个层面。
接口变慢的排查思路
大部分情况下,接口变慢的排查思路按照以下的几点来回答面试官(注意,不要过度发散)
- 利用服务的监控 (目前大部分公司项目都部署在云服务平台上,都会提供基础的监控大屏),或使用监控工具(uprometheus+Graana、2abbix等)查看系统的 CPU、内东、磁盘、网络等硬件资源的使用情况,看看是否有资源蔽颈。
- 检查网络是否存在延迟或带宽瓶颈(这个很常见,因为流量大了带宽打满了),特别是接口请求和响应涉及到跨服务、跨地区的调用。
- 查看接口的日志,确认接口是否一直慢,或者是否只在某些时间段或特定请求下变慢。
- 观察数据库情况,查询数据库的资源水平与是否存在慢査询、索引缺失或数据库锁等问题。
经过上面几个部分大致已经能确定方向,如果怀疑可能是某个新上的功能导致的,可以定位查看代码是否有性能瓶颈,例是否有不必要的同步、循环、递归等,或者某些操作存在高时间复杂度,
大部分情况下可能导致接口变慢的原因如下:
资源瓶颈
- CPU 使用过高:高CPU使用可能导致应用的计算能力受到限制,特别是当有计算密集型任务时(如加密、解密、大规模数据处理等)。可以使用 top、htop(inux) 查看系统的CPU 占用率,查看消耗 CPU 的进程,
- 内存泄漏:内存池漏导致系统内存逐渐被消耗完,最终触发GC(垃圾回收)频繁发生,导致接口响应慢,可以使用 JVM 内存分析工具(jvisualvm、Jprofiler)来检测内存占用,查看是否存在内存泄漏或须繁的垃圾回收。
- 磁盘IO负载过高:高磁盘 IO可能导致系统响应变慢,尤其是在需要频繁读写磁盘(如数据库误作、日志写入等)的场最中,需要查看磁盘的使用情况,使用 iostat、sar 等工具查看磁盘读写性能。
- 网络带宽不足或高延迟可能导致接口响应慢:尤其是跨服务、跨区域调用时,可以使用工县如 ping、traceroute、netstat等检查网络状况(大部分情况下看云服务监控即可)。
实际上也有可能就是API请求的并发量过高,接口可能承受了超出预期的并发清求,导致系统的负载过高,响应变慢.此时可以通过负载均衡、限流等机制,控制并发请求量,避免单一接口被过多请求压垮
数据库问题
(业务上很多时候都是数据库问题导致的接口慢)
- 数据库查询慢:数据库查询过慢是接口变慢的常见原因! 可能是由于 复杂的 SQL 查询、缺乏合适的索引或 表锁 导致的。可以检查数据库慢查询日志,查看是否有长时间执行的 SQL查询;使用 EXPLAIN 查看 SQL执行计划,分析是否存在全表扫描或缺少索引。
- 数据库连接池配置问题:数据库连接池的配置不合理,导致连接数不足或连接池过多,进而影响数据库性能。需要检查数据库连接池的配置,查看是否存在连接池耗尽或连接过多的情况。使用 HikariCp、Druid 等数据库连接池的监控功能查看连接池的状态,
- 数据库锁竞争:如果多个请求争夺数据库中的同一行或表,可能会导致锁竞争,从而使接口响应时间变慢。使用 SHOW ENGINEINNODB STATUS(MYSQL)查看当前数据库的锁信息,分析是否有锁竞争的情况
代码性能问题
- 高时间复杂度的算法:某些算法的时间复杂度较高,尤其是在处理大量数据时,可能导致接口响应缓慢,需要检查代码(定位最近发版的代码)中是否存在循环、递归、排序等操作,尤其是在处理大规模数据时。使用JProfiler 等工具进行性能分析,查看热点代码。
- 频繁的同步或死锁:使用同步机制(如synchronized、Reentrantlock)可能导致线程阻塞,影响并发性能。需要检查代码中是否有不必要的同步,分析是否存在死锁或高竞争的情况,要降低锁的粒度等
- 缓存未命中或缓存过期:如果系统频繁访问数据库,而没有使用缓存,或者缓存配置不当,也可能导致接口响应慢,需要检查缓存的配置和命中率分析缓存的有效性。
外部服务依赖问题
外部服务的响应慢:如果接口调用了外部服务(如支付网关、第三方 API 等),外部服务响应慢会直接影响接口的响应时间。
检查外部服务的响应时间,可以使用 超时机制 和 熔断器 来处理外部依赖失败的情况。
实际上也有可能就是API请求的并发量过高,接口可能承受了超出预期的并发请求,导致系统的负载过高,响应变慢。
此时可以通过负载均衡、限流等机制,控制并发请求量,避免单一接口被过多请求压垮。
如果线上接口被恶意刷流量了,要如何解决?
恶意刷流量是啥?就是机器模拟大量请求占用资源,刷接口、刷带宽等。
第一步是快速止血:通过网关或 CDN 设置限流、封IP、拉黑 UA,拦截恶意请求。
止血后,考虑补充防刷方案的完备性,比如接入验证码、滑块、人机验证。同时需要建立限流+ 黑名单 + 行为分析机制整一套体系。
系统每天晚上都会有一小时左右的时间瘫痪,你觉得可能的原因是什么?有遇到过这样的情况吗?
系统规律性瘫痪(每天固定时段)的核心原因大部分与周期性操作或资源瓶颈有关。
常见原因包括:
- 定时任务/批处理:夜间运行的定时任务(如数据对账、报表生成、批量更新)可能触发高CPU/内存占用、数据库锁竞争或IO瓶颈
- 资源周期性耗尽:如缓存失效(缓存击穿)、连接池/线程池被占满(夜间批量请求)、数据库连接不够用。
- 外部依赖异常:第三方系统在夜间维护 (如接口限流、服务降级),或消息队列堆积(夜间生产端集中发消息)。
- 夜间运维策略(如自动扩缩容关闭)或日志切割导致问题。
上面这几点几乎涵盖了大部分系统规律性瘫痪的原因。不过针对这个问题,这样答还不够,最好是顺着面试官的提问答,我之前在 x 项目中也遇到时这样的情况,这才是满分回答(要是设遇到过显得没有实战经验)。
举例:我之前xx系统每晚 22-23 点瘫痪,表现为页面打不开、接口超时。
我的排查步骤如下:
- 看监控:查看CPU/内存/磁盘/网络监控,发现22点后数据库CPU从20%飙升至95%,慢查询数激增
- 查日志:数据库慢查询日志显示,22点有大量 update order set status=4 where createtime < '2025-10-01'语句(归档历史订单),每条耗时5秒以上
- 定位任务:检查定时任务调度平台(如XXL-Job),发现该归档任务每天22点启动,无分页(一次性更新100万条数据),导致长事务和行锁竞争。
定位问题后,我的解决方法如下:
- 将 UPDATE 改为分页(每次更新1000条),减少单次锁持有时间.
- 在create time 字段加索引,将查询时间从5秒缩短至50ms
- 将归档任务迁移到独立数据库从库,避免影响主库业务查询。
实际上还有很多场景,我再举个例子:
我之前xx系统每晚 22-23 点瘫痪,表现为页面打不开、接口超时。看监控,发现 CPU利用率星 20 分钟周期性波动,后面找20分钟的定时任务,发现没找到,于是进一步缩短时间查找频率更高的定时任务,发现首页缓存过期时间为10分钟,但是用新缓存需要15分钟,因为随着功能的增加。
首页需要查询的内容变多,SQL 的复杂度变高,导致同新的时间超过了缓存的过期时间,于是有很多请求天法命中缓存,穿透到了数据库,上一次的刷新未完成,下一次刷新又启动,导致缓存刷解任务重叠,CPU负载叠加。
后面我进行了针对性优化,第一时间先调整了缓存的过期时间,防止穿透。
随后将首页复杂查询拆分为独立模块(如商品、推荐、排行榜),分别缓存,缩短单个缓存刷新时间(从 15 分钟一5分钟),避免任务重叠,针对部分高频查询字段(如CreateTime、UserState)等添加索引。还做了一个简单版的首页静态页面,做了降级的方案,如果网关监控到超时,直接返回静态页面作为兜底,实现降级,减少对其他功能的影响,
扩展知识
夜间是系统低峰期,很多团队会把耗时任务(如全量数据同步、历史数据归档)安排在此时运行。但如果任务设计不合理,可能直接拖垮系统:
典型问题:
- 数据库锁竞争:任务用 for 循环逐条更新数据,导致长事务或行锁/表锁,其他业务请求被阻塞(如查询变慢、超时)。
- 内存溢出:任务一次性加载百万条数据到内存(如 select * from big_table),导致JVM堆内存耗尽,触发频繁GC甚至OOM
- CPU飙升:任务包含大量计算(如复杂报表聚合),CPU使用率长期90%+,其他线程无法获取资源。
我之前遇到过一个系统,每晚23点后瘫痪,排查发现运维在23点启动了数据库全量备份(mysgdump),导致磁盘IO被占满(备份写盘占用90%+IO资源),业务查询的SELECT 语句因磁盘读取变慢而超时。还有缓存/连接池这种资源问题:
- 缓存击穿:夜间某热点Key(如爆款商品信息)集中失效,大量请求直接打穿到数据库,数据库QPS瞬间飙升,无法处理。
- 连接池耗尽:定时任务调用第三方接口时,未正确释放HTTP连接(如忘记关闭 httpclient 连接),导致连接池被占满,后续请求无连接可用,报ConnectionTimeout 。
- 线程池拥堵:任务提交到业务线程池(非独立线程池),大量任务排队导致线程池满,正常业务请求被拒绝(如 RejectedExecutionExceotion)。
有时候还可能是外部系统的影响导致的:
比如夜间任务高频调用第三方接口(如每秒钟1000次),触发对方的限流策略(如每分钟仅允许50次),导致接口大量返回429 To Many Requests,业务逻辑因重试进一步加剧负载
也有可能是第三方这个时间段高频被调,导致它们请求响应变慢,而恰好我们这边没有设置好请求的超时时间,导致请求线程长时间被阻塞,随后业务线程池满了,从而影响整个系统,
系统上线后,发现某个接口响应很慢,你如何定位可能的原因,以及对应的解决思路?
一般定位接口响应慢的核心思路是分层排查+指标监控,要从前端到后端,从应用到数据库,逐步锁定瓶领点
- 确认接口总耗时与子耗时:用APM工具(skwalking、pinpoint)看接口总耗时,拆解各环节耗时(比如数据库查询占80%、外部调用占15%、业务逻辑占5%),快速定位 耗时大头 在哪
- 应用层排查:可以使用 Arthas等工具看代码执行链路,检查是否有循环查库、重复计算、锁竞争(如synchronized锁范围过大)、对象频繁创建、触发GC(垃圾回收)等问题
- 数据库排査:查慢 SQL日志,看是否有全表扫描、索引失效、事务过长,同时检査连接池是否耗尽(比如连接数不够导致排队等待)
- 外部依赖排查:查看调用第三方接口的耗时,确认是否超时或响应不稳定,检查是否有不合理的重试逻辑(比如重复调用失败接口)。
代码逻辑问题
- 循环里查库:比如遍历 1000 条数据,每条都调一次数据库(调用数据库相对而言是一个比较重的操作)。
- 锁竞争:多个线程抢同一把锁,导致部分线程等待(比如用synchronized锁了整个方法,而实际只需锁关键代码段).
- GC频繁:短时间内创建大量对象(如循环里new对象),JVM 频繁触发FullGC,线程暂停(STW)。
解决方式:
- 批量查库:把循环里的多次查询改成一次批量查询(如用IN语句)
- 锁竞争:多个线程抢同一把锁,导致部分线程等待(比如用synchronized锁了整个方法,而实际只需锁关键代码段)。
- 优化对象创建:复用对象(如用ThreadLocal传递)或减少临时对象(如字符串拼接用StringBuilder)
数据库 SQL 与连接问题
- 慢SQL:没索引(如 WHERE条件字段无索引)、索引失效(如对字段用函数,WHERE age+1 > 20)、大表JOIN(连表字段无索引)
- 连接池耗尽:业务并发高,但数据库连接池配置太小(比如默认8个连接),线程排队等连接
- 长事务:事务执行时间过长(比如边查边改,锁表时间久),阻塞其他查询
解决方式:
- 优化SQL:用 EXPLAIN 分析执行计划,加索引(如给WHERE/JOIN字段加索引),避免全表扫描。
- 调整连接池:根据业务并发调大连接数(如调整为50),但别超过数据库最大连接限制
- 拆分事务:长事务拆成短事务(比如先查数据,再批量更新,避免边查边改)
第三方接口问题
- 第三方接口超时:对方服务响应慢(比如3s才返回),而本地没设超时时间
- 重复调用:本地重试策略不合理(比如失败后重试10次,每次等3s),总耗时30s
解决方式:
- 设超时时间:调用第三方时设合理超时(如1s),超时后快速失败
- 限制重试次数:重试不超过3次,且用指数退避(第一次等1s,第二次等2s)
- 加缓存/降级:高频调用的接口结果缓存(如Redis存5分钟),失败时返回降级数据(如默认值)
其他可能问题,比如服务器资源瓶颈
- CPU密集:接口里有复杂计算(如大量循环、加密算法),CPU使用率90%以上
- 内存不足:对象没及时回收,内存溢出导致频繁GC
- 磁盘IO高:接口里有频繁的文件读写(如日志写入),磁盘 IO 队列堆积
解决方式:
- 异步处理:把计算逻辑丢到线程池异步执行(如用CompletableFuture),接口只返回“处理中”
- 优化内存:查内存泄漏(用JProfiler),或调大JVM堆内存
- 日志异步写:用Logback的AsyncAppender,避免同步写磁盘阻塞主线程
我们线上软件安装包地址,这几天访问量暴增,现在 CDN 流量一直异常,这是什么原因?如何解决?
可能是下载地址被第三方滥用,比如被放到资源站、应用市场或被爬虫刷流量,造成流量暴涨。
解决方案主要有:加防盗链、设置访问频控、配置 CDN 限速/黑名单、或用动态签名 URL 控制有效期。
也有可能就是突然火了,业务激增,这些都是用户的正常增长。不过,所以,还是需要查看下载量和用户数是否匹配,如果差距很大,那应该就是攻击了。可以通过 CDN 日志分析流量来源,再针对性采取防盗链、封禁恶意IP、扩容节点或联系第三方平台下架链接等措施
线上 CPU 飙高如何排查?
线上CPU 飙高是一个比较常贝的问题,并且它的排查手段比较流程化,非常适合套在各个项目中,大家按照我下面的思路简单加点细节就可以使用在自己的项目上了。
- 首先确认哪个进程占用 CPU 过高,登录服务器利用 top 命令查看。
top 命令是 Linux 下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于 Windows 的任务管理器,这里需要着重了解怎么看,load average 的作用等,详情可以看top命令
- 确认 CPU 利用率很高的进程的 PID,假设为 1234 确实是Java 进程,则通过 top -Hp 1234 查看具体的线程。
- 假设得到的线程ID 是 5678,再将线程转为十六进制。 printf "%x\n” 5678
- 得到十六进制的 tid为162e,此时在利用 jstack 1234 | grep 162e -A 100 查看具体的栈信息。jstack命今用于生成虚拟机当前时刻的线程快照。线程快照是当前虚拟机内每一条线程上在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、 请求外部资源导致的长时间等待等问题
- 根据堆栈信息就可以定位到具体是哪行代码导致了CPU 高,对应分析修复即可!
常见可导致 CPU 飙高的代码问题
这里列举一些常见的代码问题,便于大家套用,例如:频繁实例化重对象(实例化过程很重,例如发号器实例、缓存实例)、一些算法复杂度很高的操作 (例如套用了3层以上的for循不)、死循环或者错误的递归调用、频繁的new对象导致频繁垃圾回收等等。
CPU 飙得很厉害了,导致 ssh 都连不上?
一般情况下,CPU 即使 100%,ssh还是能连上的,只是会有点卡。
其次一般云服务提供产商它不依赖SSH,类似一种提供了图形化的控制方法,让你可以像直接使用物理机一样访问和控制远程服务器。再则一般现在服务都是弹性扩缩容的,也就是 CPU在达到一定阈值的时候,已经扩容了,例如 CPU 在一定的时间窗口内都持续在 80%则增加机器扩容。如果代码有问题,则在新增的机器上监控一段时间也能定位到问题
线上 CPU Load 飙高如何排查?
前置知识:先区分“系统负载(Load)”和“CPU 使用率”
- Load:表示等待 CPU 运行的任务队列长度(包括正在运行和等待运行的进程数),Load 高可能是 CPU 忙不过来(如计算密集型任务),或任务太多(如并发请求激增)。C
- PU使用率:表示 CPU 实际被占用的时间比例(用户态/内核态)。
排查CPU Load飙高的关键是“从系统到进程,从进程到线程,从线程到代码”的逐层拆解,咱们主要利用工具和日志定位具体问题。
第一步要做的:用 top 或 htop 命令看 Load 数值(1分钟5分钟/15分钟的负载),同时观察 CPU 使用率(%us用户态,%sy内核态),判断是"CPU 真的在忙”还是“任务排队等 CPU"
第二步要做的:定位高负载的讲程,用 top 按 CPU 排序(按p键),找到 CPU 占用最高的进程(PID)。如果多个进程 CPU高,优先排查异常进程(如突然出现的末知进程,或业务核心进程)。
第三步要做的:定位进程内的“问题线程”,因为一般进程由多个线程组成,需进一步定位具体线程
- 用
ps -mp <PID> -o THREAD,tid,time(或pidstat -t -p <PID>)查看进程内线程的 CPU 占用,找到 CPU 高的线程 ID(TID)。 - 将 TID 转成十六进制(如
printf "%x\n" <TID>),用于后续分析。
第四步要做的:分析线程行为,定位代码问题,根据进程类型(Java/C/Go等),用工具抓线程快照
- Java进程:用
jstack <PID> | grep <十六进制TID> -A 30,查看线程的堆栈信息(如是否卡在死循环、锁等待、GC等) - C/C++进程:用
gdb -p <PID>attach进程,打印线程堆栈(thread apply all bt),看是否在某个函数循环执行。
通用方法:结合日志(如应用日志、GC日志)或监控(如Prometheus的CPU指标),确认是否有异常逻辑(如正则表达式匹配超时、频繁序列化等)
基本上排查逻辑都是如此,没实际排查过也不用虚,可以就直接按上面的方式和面试官说出来
常见的CPU Load高的原因
这里列举几个常见的原因,大家可以结合自己的项目“安”上去。
- 死循环/无限递归:代码中因逻辑错误(如未正确终止条件)导致线程持续空转,CPU使用率直接拉满(%us高)
例子:Java 中一个 while(true)循环的 break一直没触发,线程一直占用CPU。
有些同学可以能会疑惑,死循环不是应该 cpu 利用率高吗?和 load 有关系?借着这个问题我们再来盘一盘Load 还是 CPU 利用率问题
先说结论:死循环会同时导致 CPU 利用率高和Load 高,但要看“线程数”和“CPU 核心数”的关系。
- CPU 利用率:CPU“忙”的时间占比(比如一个核心 100% 表示它一直在干活)。
- 系统负载Load:等待CPU运行的任务数(包括正在运行和排队的)。比如4核 CPU,Load=4表示刚好4个任务同时跑;Load=8 表示4个在跑,4个排队等
假设你有一个单线程死循环(比如 while(true){} ),运行在单核CPU上:
- CPU 会被这个线程占满,利用率100%.
- 此时没有其他任务排队(只有这一个线程在跑),所以Load≈1(等于核心数)
但如果是8个死循环线程,运行在4核CPU上:
- 4个核心同时跑4个线程,CPU利用率100%(每个核心都在忙)
- 剩下的4个线程需要排队等 CPU,此时Load ≈ 8(4个运行+4个等待)。
所以,死循环的本质是让CPU“一直忙”(利用率高),但如果线程数超过核心数,就会导致任务排队(Load高),所以“死循环导致 CPU 利用率100%”是对的,但如果线程数过多,Load 也会跟着高
所以,线上排查时,必须同时看Load和CPU利用率
- 如果Load高+CPU利用率高:说明CPU真的忙不过来(比如死循环线程太多、计算任务重)。
- 如果Load高+CPU利用率低:说明任务在“等其他资源”(比如锁、IO慢),CPU其实很闲,但任务卡在别处
- 频繁GC(Java):堆内存不足或对象存活时间长,触发频繁 Full GC,GC 线程占用大量 CPU(%us高,可能伴随STW)。
因为 GC 太频繁(比如每秒一次Full GC),业务线程会被暂停(STW),导致请求堆积(比如用户请求无法及时处理,任务队列变长),Load 可能就会上升
内核态CPU高(%sy高):可能是系统调用频繁(如大量IO操作、网络通信),或驱动/硬件问题(如网卡中断过多)。比如内核在帮应用处理IO (比如烤贝数据、上下文切换),%sy 会升高,同时如果 IO 操作很慢(比如磁盘性能差),应用线程会等待 IO 完成,导致任务排队 (如10个线程都在等磁盘读,CPU需要轮流外理这些等待的任务),Load上升。
锁竞争:多个线程争夺同一把锁,导致线程处于 BLOCKED 状态(不占用CPU),大量线程卡在锁等待,任务队列里全是“等锁”的任务(虽然不跑,但占着队列位置),导致Load上升(比加10个线理抢同一把锁,Load可能到100)
这就是任务队列堆积 Load 高但 CPU 使用率可能不高的场景(如果面试官问什么情况下 Load 高但 CPU 使用率不高,就可以回答锁竞争这个场景)
4C8G和8C16G,这两个配置的服务器你们选哪个?为什么?
选 4C8G 还是 8C16G,核心看两个指标:内存瓶颈和 CPU 瓶颈,优先解决短板。
- 如果目前 Java 服务内存需求小、CPU利用率低,典型如轻量级Web应用则考虑 4C8G。
- 如果目前 Java 服务内存消耗大或CPU密集,典型如高并发API网关、复杂计算服务,则考虑 8C16G。
实际上,大部分的公司的服务采用 4C8G 足以。主要由以下几个因素
- GC 问题:Java 堆内存并不是越大越好。虽然堆内存大,可以减少 GC的次数,但相反会增加每次 GC停顿的时间(STW)。所以一般的 Java 应用 4C8G 足以。
- 资源利用率问题:2台4C8G 的资源和一台 8C16G 的资源实际上是一样的。但是很多应用实际上用不上大内存或者这么多的CPU,换句话说小资源就够用了。所以从资源利用率的情况来说,对于很多普通Java 应用来说,8C16G分为2台利用率更高。
- 负载均衡:如果总资源一样,4C8G 的机器数会更多一些,资源可以更均匀的负载到多个服务器上,倘若部分服务器出现故障,影响面也会比较小
- 其他:有些公司会限制单个应用的一些配置,比如数据库、缓存等连接数,这时候总资源一致的情况下,4C8G 能用上更多的连接数,8C16G 则比较少。
综上,大部分公司的应用都是 4C8G,如果内存或 CPU 只要有一个要求较高,则就需要换成 8C16G。
从网关再到各个后端服务,如何设置 RPC 的超时时间,要考虑哪些问题?
首先用户(客户端)的请求是先打到网关,再由网关请求各后端服务,因此需要确保客户端到网络连接的超时时间(通常是 HTTP请求),需要大于网关调用后端服务(通常是 RPC 请求)的综合时间。
其中又有一些细节需要考虑
- 网关调用几个服务的方式
网关调用后端需求可以是串行,也可以是并行,假设请求服务A需要花费 1、请求服务 B花费 2s、请求服务 C 花费 3s
如果是串行,那么网关需要等待服务A响应后,再调用服务 B,拿到服务B的结果后,再调用服务C,总时间是 1+2+3-6。
如果是并行,则网关可以并发请求三个服务,最终的花费的时间以最慢的那个服务的响应为准,花费的时间是 3s。
因此不同的调用方式,计算的超时时间是不同的。
- 考虑每个服务TP99(或TP95)的耗时
除了串行并行之外,每个后端服务对请求的响应时间,也需要有个大致的估算,估算的值不能是凭空而来,而是利用每个服务 TP99(或IP95)的耗时(TP99 指的是百分之九十九的调用所需的最高耗时)。
通过这个方式来得来的调用耗时才比较精准和专业。
- 考虑超时重试次数
为了避免网络抖动的影响,一般请求都需要有重试机制,比如重试2次,那么实际上最多会有3次调用(正常1次+重试2次),最长的耗时就是超时时间*3,所以重试的时间也需要计算在总的超时时间之内这种问题是无法量化的
你可千万别说个什么设置 5s 这种,需要根据业务场景具体问题具体分析,我们仅需回答到上面这些关键点即可。
当客户端有重连机制,当服务意外宕机重新部署启动时,怎么避免流量洪峰?
对于大流量场景,主要采用以下几个方式来应对服务的异常恢复:
- 服务端限流: 服务主动限流,启动时设置较低的连接/请求阈值,如只放行10%流量,随着服务状态稳定,如内存加载完成、缓存预热达标,逐步提升比例,让客户端分批重连,可以用Sentinel实现动态限流,实际操作中,如果是大规模重启,还可以通过地区放行,例如华东、华北这种来放行。
- 客户端随机退避:即客户端重连机制中加入随机退避时间,比如基础等待时间+30%随机值。避免同时涌来,且客户端在多次失败后进入冷静期或直接熔断一段时间,防止对不可用服务持续攻击。
- 预加载(预热):提前从DB或缓存加载客户端的历史连接状态,如会话信息、配置参数,避免重连时重复计算,如重新鉴权、查询用户信息,这样客户端重连只需核对状态,减少服务端计算压力。
新项目要上线了,你会关注哪些指标,为什么?
我会重点关注
- 性能指标,比如 QPS、响应时间、吞吐量。
- 错误指标,比如错误率、异常日志量。
- 资源指标,比如 CPU/内存使用率、磁盘10、网络带宽。
- 还有业务指标,比如 PV/UV、接口调用量:
因为,这些指标能帮我判断系统是否稳定、能否承载预期流量、资源是否合理利用,以及业务流程是否顺畅等。
比效 QPS 反映系统处理能力,错误率体现服务可靠性,CPU 使用率过高可能是硬件或代码存在瓶颈,业务指标能直接验证新项目功能是否达到用户预期。
一条 1s 请求的响应,怎么优化成 1ms?
实际 1ms 的响应还是比较苛刻的,一一般来说要达到这个级别的响应,需要用缓存存储结果,直接返回数据。比如可以用外部存储如 Redis,甚至于本地缓存。
部分方法还需要同步转异步。异步处理将耗时操作(如数据库查询)交给后台线程,立刻返回“处理中”响应(返回一个future对象),这样 1ms 内就能拿到响应,实际任务在后台执行。
大致就是这两类,实际上我们要优化方法是需要先定位阻塞点的。
比如通过性能分析工具(如 Java 的 JProfiler,其至简单时间戳都行)来定位耗时环节,例如网络延迟、CPU 计算或 IO 等待。
然后,发现关键优化点。例如用缓存避免重复计算(Redis或本地缓存)、采用异步处理或非阻塞调用分解耗时任务、优化算法和数据结构提升执行效率、并减少不必要的外部 IO 操作。
最后,确保系统环境处于最优情况。比如网络架构,外网ip换成内网ip,跨机房换成同机房等等。
有人恶意攻击我们的注册功能,伪造手机号大量下发验证码,除了限流之外,如何解决这个问题?
除了限流外,可以通过图形验证码、人机验证(如滑动验证)、设备指纹识别、黑名单机制、手机号风控校验等手段组合使用,从多个维度验证请求是否合法。
- 图形验证码/滑动验证:在发送短信验证码前,先加上一道人机验证,如滑块、拼图验证码。这道坎基本能挡住绝大多数脚本刷验证码的行为。
- 设备指纹识别:攻击者可能伪造多个手机号,但用的是同一台设备。所以,我们可以通过,浏览器 UA + 屏幕分辨率 + 时区 +IP 等生成设备指纹限制每台设备每天发送次数
- 手机号归属地 + 风控库校验:判断手机号是否是虚拟号段(如 170、171,很多是网关设备)。可以调用风控平台或运营商 API,识别风险号码些云平台,如腾讯云天御、阿里云盾都支持手机号命中风险库校验。
- 监控异常行为与黑名单:可以实时记录手机号、IP、User-Agent 的请求行为。如果发现以下行为,可自动拉黑、报警或封禁。
- 同一个IP 5 分钟内请求了几十个不同手机号
- 某段手机号集中被请求
- 同一设备行为异常。
