一、先搞懂啥是“连接风暴”?为啥会拖垮系统?
咱们先从大家都碰过的场景说:比如你家小区楼下的快递柜,平时快递员来存件,一个一个来,柜管系统能轻松应付;但要是双十一前一天,所有快递员都挤到快递柜,一秒钟来几百个存件请求,柜管系统瞬间卡死——这就是快递柜版的“连接风暴”。
换到我们说的系统里,就是应用程序(比如你写的Java服务)要连数据库(这里是OceanBase),本来大家都规规矩矩排队连,结果突然某个应用出问题,或者上线改了个配置,导致几百、几千个连接请求一股脑往中间层(OBProxy)冲,把OBProxy的资源占满,后面正常的连接请求根本进不来,哪怕连进来也慢得要死,这就是“连接风暴导致路由延迟”。
为啥会出这种事?举个最常见的例子:
1.1 一个真实的连接风暴触发案例
比如我们有个电商的订单服务,用的是OceanBase做订单数据库,中间层是OBProxy。之前上线了一个定时任务,负责同步每日订单数据,本来这个任务是每天凌晨1点跑,结果上线时运维改配置漏了个参数,把任务的执行时间改成了“每5分钟跑一次”,而且任务里的数据库连接池配置是“每次执行都新建100个连接”。
这就出大问题了:每5分钟,这个任务就往OBProxy发100个连接请求,要是刚好有两个任务碰一起(比如前一个任务还没跑完,后一个又触发了),就是200个;要是刚好赶上订单峰值,应用本身的正常连接又多,那OBProxy瞬间就被占满了。
这时候会发生啥?比如用户下单,本来要连数据库写订单,结果OBProxy里的连接资源满了,新的连接请求要等半天,甚至直接超时,用户就会看到“下单失败”或者“页面加载半天没反应”。
二、连接池是啥?为啥它是“第一道防线”?
你可以把连接池理解成应用和数据库之间的“连接仓库”:应用不用每次要连数据库都去“开新门”(新建一个TCP连接),而是提前从仓库里拿一个已经开好的门(连接),用完再放回去,这样既快又省资源。
连接池的配置直接决定了会不会触发连接风暴,比如刚才的定时任务,要是连接池配置得好,就不会每5分钟新建100个连接,而是复用之前的。
2.1 连接池的核心配置(用HikariCP举例子)
这里我们用Java最常用的连接池HikariCP,给大家看几个核心配置,每个配置都有注释,一看就懂:
// 连接池核心配置示例(Java)
public class DbConfig {
// 连接池最大连接数:最多能有多少个连接同时在使用
private static final int MAX_POOL_SIZE = 20;
// 连接池最小空闲连接数:平时留多少个连接备用,不用每次都新建
private static final int MIN_IDLE = 5;
// 连接超时时间:拿连接时最多等多久(单位:毫秒),超过就报错
private static final int CONNECTION_TIMEOUT = 3000;
// 空闲连接超时时间:连接闲置多久就回收(单位:毫秒)
private static final int IDLE_TIMEOUT = 60000;
// 最大连接生命周期:连接用多久就强制回收(单位:毫秒),避免连接老化
private static final int MAX_LIFETIME = 1800000;
public HikariConfig getHikariConfig() {
HikariConfig config = new HikariConfig();
// 数据库地址:这里是OBProxy的地址,不是OceanBase的直接地址
config.setJdbcUrl("jdbc:mysql://obproxy-host:2883/order_db");
config.setUsername("order_user");
config.setPassword("order_pass");
// 把上面的配置填进去
config.setMaximumPoolSize(MAX_POOL_SIZE);
config.setMinimumIdle(MIN_IDLE);
config.setConnectionTimeout(CONNECTION_TIMEOUT);
config.setIdleTimeout(IDLE_TIMEOUT);
config.setMaxLifetime(MAX_LIFETIME);
return config;
}
}
这里的MAX_POOL_SIZE就是关键:要是你把这个值设成100,那这个应用最多只能同时拿20个连接,不会一下子冲100个去OBProxy;要是设成0或者很大,那应用就会无限制新建连接,很容易触发风暴。
2.2 连接池的限流逻辑
连接池的限流其实就是“不让你拿太多连接”:比如上面的配置,最多20个,要是已经有20个连接在被使用,新的请求来拿连接,就会先等3秒(CONNECTION_TIMEOUT),要是3秒内还没人还连接,就直接抛错,不会一直等,也不会再新建更多连接。
这个逻辑很重要:要是没有这个限制,新请求会一直新建连接,直到把OBProxy搞垮。
三、OBProxy的限流降级:“最后一道防线”
要是连接池的防线破了(比如某个应用的连接池配置错了,或者多个应用同时出问题),那OBProxy就得自己扛了——这时候就需要OBProxy的限流降级策略,来保护自己和后面的OceanBase集群。
OBProxy的限流降级分好几种,我们一个个说,都是能直接用的配置。
3.1 连接数限流:控制每个应用能拿多少连接
这个是最直接的:给每个连接OBProxy的应用(也就是客户端)设一个最大连接数,超过这个数,新的连接请求就会被拒绝。
比如我们给订单服务设最大连接数是50,给用户服务设最大连接数是30,这样就算订单服务的连接池出问题,最多也只能连50个,不会影响其他服务。
这个配置要在OBProxy上改,我们用OBProxy的配置文件举例子:
// OBProxy的客户端连接限流配置(JSON格式)
{
"client_limits": [
{
// 应用的用户名:就是连接OBProxy时用的用户名
"user": "order_user",
// 这个用户最多能同时有多少个连接
"max_connections": 50,
// 超过限制时的处理方式:1=拒绝新连接,0=允许(默认)
"limit_type": 1
},
{
"user": "user_user",
"max_connections": 30,
"limit_type": 1
}
]
}
改完这个配置,OBProxy会实时统计每个用户的连接数,要是超过max_connections,新的连接请求就会直接收到“Too many connections”的错误,不会再占用OBProxy的资源。
3.2 QPS限流:控制每个应用每秒能发多少请求
除了连接数,OBProxy还能限制每个应用每秒能发多少SQL请求(QPS)。比如订单服务每秒最多发1000个请求,超过的话,要么排队,要么直接报错。
这个配置适合那种“连接数没超,但请求太多把OBProxy搞卡”的场景,比如某个应用突然发了个批量查询,一下子冲了几万个请求。
OBProxy的QPS限流配置示例:
// OBProxy的QPS限流配置(JSON格式)
{
"qps_limits": [
{
// 应用的用户名
"user": "order_user",
// 每秒最多允许多少个请求
"max_qps": 1000,
// 超过限制时的处理方式:1=限流(排队),2=拒绝,0=允许
"limit_type": 1
}
]
}
要是设成limit_type:1,超过的请求会先等一会儿(排队),要是排队的时间太长,也会报错;要是设成limit_type:2,超过的请求直接拒绝。
3.3 降级策略:极端情况的“保命手段”
要是OBProxy的资源快被占满了(比如CPU到了90%,内存到了80%),就会触发降级策略,主动放弃一些不重要的请求,保证核心请求能正常处理。
比如:
- 先拒绝所有非核心应用的连接请求
- 限制核心应用的QPS,只允许核心请求(比如下单、支付)通过
- 暂时关闭一些非核心的功能(比如查询历史订单)
OBProxy的降级配置示例:
// OBProxy的降级配置(JSON格式)
{
"degrade": {
// 触发降级的CPU阈值(百分比)
"cpu_threshold": 90,
// 触发降级的内存阈值(百分比)
"mem_threshold": 80,
// 降级时的处理逻辑:1=先拒绝非核心用户,2=限制核心用户的QPS
"degrade_type": 1,
// 核心用户列表:这些用户的请求不会被降级
"core_users": ["order_user", "pay_user"]
}
}
比如CPU到了90%,OBProxy就会先拒绝除了order_user和pay_user之外的所有用户的新连接,保证下单和支付的请求能正常处理。
四、应用场景、优缺点、注意事项
4.1 应用场景
这些策略适合所有用OBProxy中间层的场景,尤其是:
- 电商、金融等对可用性要求高的系统:比如电商的下单、支付场景,不能因为连接风暴导致服务不可用
- 多应用共享一个OBProxy的场景:比如多个业务线的应用都连同一个OBProxy,需要互相隔离,避免一个业务出问题影响其他
- 有定时任务、批量任务的场景:比如定时同步数据、批量查询订单的任务,容易触发连接风暴
4.2 技术优缺点
| 策略类型 | 优点 | 缺点 |
|---|---|---|
| 连接池限流 | 1. 最基础的防线,提前控制应用的连接数;2. 配置简单,容易理解 | 1. 要是多个应用的连接池都配置错了,还是会触发风暴;2. 只能限制单个应用的连接数,不能限制总连接数 |
| OBProxy连接数限流 | 1. 全局控制每个应用的连接数,不会被单个应用搞垮;2. 可以隔离不同应用的资源 | 1. 配置在OBProxy上,要是OBProxy本身出问题,配置就失效了;2. 不能限制请求的频率 |
| OBProxy QPS限流 | 1. 可以限制请求的频率,避免批量请求搞卡OBProxy;2. 适合那种连接数没超但请求太多的场景 | 1. 配置复杂,需要根据业务情况调整QPS值;2. 排队的请求会增加延迟 |
| OBProxy降级 | 1. 极端情况的保命手段,保证核心业务正常;2. 可以灵活配置核心用户 | 1. 会导致非核心业务暂时不可用;2. 配置不当可能会误杀核心业务 |
4.3 注意事项
- 连接池配置一定要合理:不要把
MAX_POOL_SIZE设得太大,一般根据业务的并发量来设,比如订单服务的并发量是100,那MAX_POOL_SIZE设成20就够了,不用设成100 - OBProxy的限流配置要定期更新:比如新上线了一个应用,要及时给这个应用设连接数和QPS限制,避免这个应用无限制占用资源
- 核心业务的保护:一定要把核心应用的用户名设成
core_users,降级的时候不会被影响 - 监控告警:要监控连接数、QPS、CPU、内存这些指标,要是超过阈值,及时告警,避免出问题
- 配置要测试:比如改了连接池的配置,要先在测试环境测试,看会不会影响业务,再上线到生产环境
五、文章总结
连接风暴其实就是“资源被突然占满导致的系统瘫痪”,要解决这个问题,得从两道防线入手: 第一道防线是连接池,提前控制应用的连接数,不让应用一下子冲太多连接去OBProxy; 第二道防线是OBProxy的限流降级,要是连接池的防线破了,OBProxy自己扛,控制每个应用的连接数、请求频率,极端情况还能降级,保证核心业务正常。
其实这些策略的核心就是“隔离”和“限制”:隔离不同应用的资源,不让一个应用影响其他;限制每个应用的资源使用,不让某个应用占满所有资源。
最后再给大家提个醒:上线新应用或者改配置的时候,一定要检查连接池的配置,避免因为配置错误触发连接风暴——很多时候,连接风暴不是因为系统太弱,而是因为一个小小的配置错误。
评论
围绕“OceanBase的OBProxy连接风暴导致路由延迟,从连接池到proxy的限流降级策略详解”参与讨论